[译] 误解 ES6 模块,升级 Babel 的一个解决方案(泪奔)

栏目: JavaScript · 发布时间: 7年前

内容简介:说多了都是泪...在所有这些都说明了,Babel 6.0.0 是一个非常重大的变革版本。一开始可能有点不稳定。因此升级也并不容易,需要学习。这篇文章不一定会讨论如何 Babel。我只想讨论我从自己代码中学会的内容 —— 当 Babel 修复了我的严重依赖问题时... 在尝试将 Babel 5 升级到 Babel 6 之前,希望你可以去阅读以下内容:
[译] 误解 ES6 模块,升级 Babel 的一个解决方案(泪奔)

说多了都是泪...

2015 年 10 月 29 号Sebastian McKenzie 、James Kyle 以及 Babel 团队的其他成员,发布了一个面向各地前端开发者的大型版本:Babel 6.0.0。太棒了,因为它不再是一个转译器,而是一个可插拔的 JavaScript 工具平台。作为一个社区,我们只触及了它能力的表面,我对 JavaScript 工具的未来感到兴奋(谨慎乐观态度)。

所有这些都说明了,Babel 6.0.0 是一个非常重大的变革版本。一开始可能有点不稳定。因此升级也并不容易,需要学习。这篇文章不一定会讨论如何 Babel。我只想讨论我从自己代码中学会的内容 —— 当 Babel 修复了我的严重依赖问题时... 在尝试将 Babel 5 升级到 Babel 6 之前,希望你可以去阅读以下内容:

ES6 模块

如果我可以正确理解 ES6 模块规范,对我来说,升级就不会那么困难了。Babel 5 允许滥用 exportimport 语句,Babel 6 解决了这个问题。一开始我以为这可能是 Bug。我在Stack Overflow 和Logan Smyth 上提问这个问题,反馈的信息告诉我,我从根本上误解了 ES6 模块,而且 Babel 5 助长了这种误解(编写一个转换器很困难)。

当前危机

起初,我不太明白 Logan 的意思,但当我有时间全身心投入我的应用升级时,发生了这些事情:

我疯了么?这是无效的 ES6 么?export default { foo: 'foo', bar: 'bar', }

@kentcdodds

Tyler McGinnis 、Josh Manders 和我在这个线程上测试了一下。这可能很难理解,但我意识到问题不是将对象默认导出,而是如何像预期那样可以导入该对象。

我总是可以导出一个对象作为默认值,然后从该对象中通过解构的方式获得我所需要的部分(字段),如下所示:

// foo.js
const foo = {baz: 42, bar: false}
export default foo

// bar.js
import {baz} from './foo'
复制代码

因为 Babel 5 的转换是导出默认语句,所以它允许我们这样做。然而,根据规范,这在技术上是不正确的,这也是为什么 Babel 6(正确地)删除了该功能,因为它的能力实际上是在破坏我在工作中应用程序的 200 多个模块。

当我回顾Nicolás Bevacqua 的博客时,我终于明白了它的工作原理。

当然,也要感谢@nzgb 在 ES6 上的 350 个令人惊讶的要点,因为它非常清晰 ponyfoo.com/articles/es… @rauschma

@kentcdodds

当我读到Axel Rauschmayer 的博客时,我发现为什么我一直在做内容无效。

我想感谢@rauschma 用 ES6 模块将我从早期中年危机中拯救出来。我可能对这事太专注了。。。

@kentcdodds

基本思想是:ES6 模块应该是静态可分析的(运行时不能更改该导出/导入),因此不能是动态的。在上述示例中,我可以在运行时更改 foo 的对象属性,然后我的 import 语句就可以导入该动态属性,就像这样:

// foo.js
const foo = {}
export default foo
somethingAsync().then(result => foo[result.key] = result.value)

// bar.js
import {foobar} from './foo'
复制代码

我们将假设 result.key 是 ‘foobar’。在 CommonJS 中这很好,因为 require 语句发生在运行时(在模块被需要的时候):

// foo.js
const foo = {}
module.exports = foo
somethingAsync().then(result => foo[result.key] = result.value)

// bar.js
const {foobar} = require('./foo')
复制代码

可是,因为 ES6 规范规定导入和导出必须是静态可分析的,所以你不可能在 ES6 中完成这种动态行为。

这也是 Babel 做出改变的 原因 。这样做是不太可能的,但这也是件好事。

这意味着什么?

用文字来描述这个问题确实比较困难,所以我希望一些代码的示例与对比会有指导意义。

我遇到的问题是,我将 ES6 exports 与 CommonsJS require 组合在一起。我会这样做:

// add.js
export default (x, y) => x + y

// bar.js
const three = require('./add')(1, 2)
复制代码

Babel 改变后,我有三个选择:

选择 1:默认 require

// add.js
export default (x, y) => x + y

// bar.js
const three = require('./add').default(1, 2)
复制代码

选择 2:100% 的 ES6 模块

// add.js
export default (x, y) => x + y

// bar.js
import add from './add'
const three = add(1, 2)
复制代码

选择 3:100% 的 CommonJS

// add.js
module.exports = (x, y) => x + y

// bar.js
const three = require('./add')(1, 2)
复制代码

我如何修复它?

几小时后我开始运行构建并通过了测试。不同的场景,我有两种不同的方法:

  1. 我将导出更改为 CommonJS( module.exports ),而不是 ES6( export default ),这样我就可以像一直做的那样继续 require。

  2. 我写了一个复杂的正则表达式来查找并替换(应该使用一个 codemod)那些将其他 require 语句从 require(‘./thing’) 转向 require(‘./thing’).default** 的改变。

它工作的很完美,最大的挑战就是理解 ES6 模块规范是如何工作的,Babel 如何将其转换到 CommonJS,从而实现交互操作。一旦我把问题弄清楚了,遵循这一规则来升级我的代码就变成了超简单的工作。

建议

尽量避免混合 ES6 模块和 CommonsJS。我个人而言,会尽量使用 ES6。首先,我将它们混合在一起的原因之一是我可以执行单行的 require,并立即使用所需的模块(比如 require(‘./add’)(1, 2) )。但这真的不是一个足够大的好处(就我个人看来)。

如果你觉得必须将它们组合起来,可以考虑使用以下 Babel 插件/预置之一:

结论

所有这些真正的教训是,我们应该明白事情是如何运作的。如果我理解 ES6 模块规范实际上是如何运作的,我就可以节省大量时间。

你可能会受益于这个Egghead.io 课程,我演示了如何从 Babel 5 升级到 Babel 6:

egghead.io/lessons/ang…

另外,记住,没有任何人是完美的,我们都在这里学习 :-)Twitter 上见

[译] 误解 ES6 模块,升级 Babel 的一个解决方案(泪奔)

附录

更多示例:

在对 Babel 进行更改之前,有一个像这样的 require 语句:

import add from './add'
const three = add(1, 2)
复制代码

但在 Babel 发生变化之后,Require 语句现在变得就像这样:

import * as add from './add'
const three = add.default(1, 2)
复制代码

我想,导致这个问题的原因是,add 变量不再是默认导出,而是一个拥有所有命名导出以及 default export 的对象(在默认键下)。

命名导出:

值得注意的是,你可以使用命名导出,我的建议是在 工具 模块中那么做。这允许你在 import 语句( 警告,尽管由于前面的静态分析原因,他看起来并不是真正的析构 )中执行类似于析构的语法。因此,你可以那么做:

// math.js
const add = (x, y) => x + y
const subtract = (x, y) => x - y
const multiply = (x, y) => x * y
export {add, subtract, multiply}

// foo.js
import {subtract, multiply} from './math'
复制代码

在tree shaking 的情况下,这令人兴奋,还很棒。

个人而言,我通常建议对于组件(像 React 组件或 Angular 服务)使用 default export(你知道自己要导入的待定内容,单文件,单组件 :grinning:)。但对于工具模块,通常有各种可以独立使用的纯函数。这是命名导出的一个很好的用例。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

RESTful Web Services Cookbook

RESTful Web Services Cookbook

Subbu Allamaraju / Yahoo Press / 2010-3-11 / USD 39.99

While the REST design philosophy has captured the imagination of web and enterprise developers alike, using this approach to develop real web services is no picnic. This cookbook includes more than 10......一起来看看 《RESTful Web Services Cookbook》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换