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

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

内容简介:说多了都是泪...在所有这些都说明了,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:)。但对于工具模块,通常有各种可以独立使用的纯函数。这是命名导出的一个很好的用例。


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

查看所有标签

猜你喜欢:

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

C程序设计语言

C程序设计语言

(美)Brian W. Kernighan、(美)Dennis M. Ritchie / 徐宝文、李志译、尤晋元审校 / 机械工业出版社 / 2004-1 / 30.00元

在计算机发展的历史上,没有哪一种程序设计语言像C语言这样应用广泛。本书原著即为C语言的设计者之一Dennis M.Ritchie和著名计算机科学家Brian W.Kernighan合著的一本介绍C语言的权威经典著作。我们现在见到的大量论述C语言程序设计的教材和专著均以此书为蓝本。原著第1版中介绍的C语言成为后来广泛使用的C语言版本——标准C的基础。人们熟知的“hello,World"程序就是由本书......一起来看看 《C程序设计语言》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具