为什么 ES Module 云云姗姗来迟

申明:本文宣布以后,此题目的推动峰回路转,不断有新内容。文末新增一节 Updates,跟进本文宣布以后的 ES Module 范例化愿望状况。

浏览器大战多年了热度照旧高涨,人人终究在 JS 新特征的布置上杀青一致纷纭追逐最新范例,然则 ES2015 中的 ES Module 这个万众期待的重要特征却一直迟迟未能完成。

等 2020 年回望汗青,倘使我们错过了 ES Module 这艘船而 Node.js 死在汪洋大海当中,没有任何其他手艺题目的重要性可以与此比拟。


issac

Module 的范例是落成了的,只是关于模块怎样加载和剖析留给了“完成环境决议”——按汗青经验,题目每每就出如今这一环。固然了不是烫手山芋 W3C 也不会就这么轻松甩开对吧,事实上这也不是 W3C 一家的事变,牵涉到 TC39、Node 手艺委员会、Node 和前端两个开辟社群,以及 npm 公司。

故事很长,我们从新说起。importexport 的语法范例很邃晓,模块的剖析器 V8 早已完成,万事俱备只欠加载。戋戋加载能有多贫苦?

Module 的特征

在新范例下,JavaScript 顺序划分红两种范例:剧本(我们之前写的传统JS)和模块(ES范例中新定义的 Module),模块有四项于剧本差别的特征:

  1. 强迫严厉形式(没法作废)
  2. 实行环境在一个非全局的作用域中
  3. 可以运用 import 导入其他 Module 的 binding
  4. 可以运用 export 导出本 Module 的 binding

看上去划定规矩简朴邃晓,然则要让一个剖析器(parser)辨别兼容这两种形式还挺庞杂的。

剖析器的困难

看看代码中是不是包括
import
export 关键字不就可以推断它的范例了么?

不可。起首猜想用户企图是个风险行动,假如你猜对了,就越发掩盖了猜错可能会形成的风险。

而严厉形式,除了运行时的一些请求以外还定义了几个语法毛病:

  1. 运用 with 关键字;
  2. 运用八进制字面量(如 010);
  3. 函数参数重名;
  4. 对象属性重名(仅在 ES5 环境。ES6 作废了此毛病);
  5. 运用 implementsinterfaceletpackageprivateprotectedpublicstaticyield 作为标识符。

这些语法毛病须要在剖析时就抛出来。所以假如以剧本形式剖析到了文件末端才发明有 export,就得从新从新剖析悉数文件来捕获上述语法毛病。

那我们换一条路,最先先假定为模块举行剖析代码。既然 Module 语法相称于严厉形式 + 导入导出 (importexport),我们可以用剧本形式 + 导入导出的语法来剖析悉数文件。然则这类剖析划定规矩已逾越了范例定义,这么歪曲的线路可以预感它成为 Bug 源泉的模样。

风险但不是不可能。OK 真正的贫苦来了:根据范例 importexport 都是可选的——你可以写一个 Module,既不导入也不导出任何东西,它只是对全局作用域做些小动作,比方如许:

// 一个正当的 Module
window.addEventListener("load", function() {
    console.log("Window is loaded");
});
// WAT!

总的来说,包括 importexport 表明它一定是个 Module,但没有这两个关键字却不能证实它不是 Module。 ╮(╯_╰)╭

辨别 JavaScript 文件范例的使命没法放在剖析器里自动完成,我们须要在剖析文件之前就晓得它的范例。

浏览器的方法

这就是为何浏览器的模块引用是这个写法:

<script type="module" src="foo.js"></script>

当浏览器最先加载这个 foo.js,它会边加载边剖析,遇到 import { bar } from './bar.js' 的第一时候最先加载依靠的 bar.js,加载完以后对其剖析,搜检个中是不是导出了 bar。云云来去完成悉数 Module 的剖析。

Node.js 呢

到了 Node.js,新的题目来了。

作为世界上最大的软件包堆栈,npm 中现有的软件包都是 CommonJS 范例。ES Module 须要可以与 CommonJS 模块共存,许可开辟者们逐渐转向新的语法。

所谓的共存,主如果指 import { foobar } from 'foobar' 语法要支撑 CJS Module 和 ES Module 两种包花样——假如 import 只能用来导入 ES Module 而 require 可以导入恣意模块,那末一切人都会用 require;假如 importrequire 各自担任导入各自的花样,那末开辟者就须要晓得一切依靠的库的花样,运用响应语法来导入它,并且在依靠的库们更换到新花样的时刻修正自身的代码去兼容……在可预感的 CommonJS -> ES Module 冗长过渡期里如许的累坠对社区而言不可接收。

为此社区提出了不少设计,(好音讯)经由大批的议论以后如今已集合到两个挑选还在议论:

  1. 剖析器自动检测。最大的优点是对用户而言通明,惋惜缘由如前所述,此设计已否认。
  2. 运用 "use module" 标注。一想到 JS 的将来永久都要在文件开首贴这么个膏药人人就不能忍了。否认。
  3. 新的文件后缀 .jsm。重要题目是现有社区东西链悉数须要更新才支撑,别的和浏览器完成的一致也要斟酌。
  4. package.json 上发挥。这个门类下的发起就更多了,比方增添一个 module 字段逐渐替代掉 main
{
    // ...
    "module": "lib/index.js",
    "main": "old/index.js",
    // ...
}

这个设计只实用单进口的状况,对多文件(比方 require('foo/bar.js')的场景)就不可了。那就改成 modules 字段(庞杂度陡升):

{
    // ...
    // files:
    "modules": ["lib/hello.js", "bin/hello.js"],

    // directories:
    "modules": ["lib", "bin"],

    // files and directories:
    "modules": ["lib", "bin", "special.js"],

    // if package never uses CJS Modules
    "modules": ["."],
}

这还没完,更多设计就不详述了,人人可以到 Node.js Wiki 上检察。

就个人偏好而言,只管一切的设计都有利有弊,而 package.json 这条路为了兼容种种需求,修正版的提案已愈来愈庞杂,比较起来 .jsm 后缀却是愈发显得简朴清楚了。我更喜好这个清洁的处理设计。

如今的愿望(2016.04.15)

<script type="module" />到场 HTML 范例,WhatWG 方才发了一篇文章报告他们怎样经由艰苦卓绝的勤奋杀青这一目的,接下来就看浏览器厂商完成了。

除此以外 WhatWG 手上另有一个 ES Module loader 范例,用于指定 Module 的动态加载体式格局。它曾经是 ES6 草案的一部分,但因为 ES2015 “要赶着宣布来不及了”不幸被砍,现在归属 WhatWG 推动

Node.js 这边,在相称一段时候里我们还要借助 transpiler 来体验 ES Module。这件事须要 V8、Node.js、WhatWG 配合谐和完成。

按设计本月 Node.js 宣布 6.0,顺遂的话可以 肯定集成 V8 5.0(BTW,一天后 V8 宣布了 5.1),对 ES2015 的特征支撑到达 93%——看来 ES Module 极可能会成为 “The last ES2015 feature” 了。

关注 ES Module 的愿望,还可以看看几个处所:

  1. Node 社区提案和议论:https://github.com/nodejs/nod…
  2. V8 的完成:https://bugs.chromium.org/p/v…
  3. Blink 的完成:https://bugs.chromium.org/p/c…

愿 ES Module 早日到来。

Updates

关于 ES Module 在 Node.js 环境下的辨认设计,从一月份 bmeck 提出提案最先社区就延续沟通和争辩,以下是相干愿望更新。

  • 2016.01.08
    bmeck 提出关于 ES Module 的提案(增添新后缀.mjs),社区议论最先。
  • 2016.02.06
    社区提的设计归纳起来,有四个方向
  • 2016.04.15
    本文宣布的日子。
  • 2016.04.20
    经由两个月的麋集议论,四个方向只剩下两个存活:.mjs 派和 package.json 派,然则这两派的争辩异常猛烈。
  • 2016.04.27
    鉴于 .mjs 已在正式提案中,倘使议论延续僵持不下,不出不测 .mjs 将会跟着时候推移而正式成为范例。怀着如许的危机感,package.json 派发起了 In defense of dot js 来对抗 .mjs 的提案,请求坚持 .js 后缀稳定而运用 package.json 来辨认 ES Module。
  • 2016.06.14
    严重转机!bmeck 提出一个新的设计 UnambiguousJavaScriptGrammar:既然双方的纠结都是因为没法从文件自身辨认 ES Module 而起,无妨调解一点语法细节(ES Module 中的 exports 语句不再是可选的,最少有一句 exports {} 来表明该文件是个 ES Module),两派的争辩就这么水到渠成了!
  • 2016.07.06
    经由 Node.js TSC 的议论,Unambiguous JavaScript Grammar 设计正式到场发起(proposal)
  • 2016.07.07
    虽然 Unambiguous JavaScript Grammar 到场了 Node.js 的草案提案(5.1章),然则斟酌到间隔 TC39 的七月集会只剩下一周时候,而 Node.js 这边愿望做更充足的调研和测试再举行议论,所以从此次 TC39 的议程中拿掉了
  • 2016.09.06
    Domenic 提了 import() 作为动态加载的设计,有望庖代 System.import()System.loader.import()
  • 2016.09.17
    ES Module 再次提上 TC39 的议事日程,相干的另有内建模块import()
  • 2016.09.30
    TC39 9月碰头会的与会者纷纭示意此次集会愿望使人兴奋,集会内容汇总在此,以及一些补充

    • Node.js 开辟者想要提出一些修正范例的发起,也不晓得适宜不适宜,沟通以后发明 TC39 是异常体贴和在乎每一个社区的需求的(人人相谈甚欢)。
    • 底本的 ES 范例请求模块加载历程须要先完成静态 parse 然后再 evaluate,然则如今的 Node.js CommonJS 模块没法满足这个请求(CJS 模块必需 evaluate 以后才晓得 exports 的是什么)。议论下来范例将会改成许可 parse 历程在遇到 import CJS 模块时进入一个挂起的状况,守候依靠树中的 CJS 模块 evaluate 以后再完成 parse。
    • 对模块范例的检测现在是三个设计选项:

      1. Unambiguous JavaScript Grammar 看上去比较简朴,但完成起来照样有不少坑;
      2. package.json 的方法比较累坠,范围也多;
      3. .mjs 的设计最简朴,看来是最可行的,而且也跟 Node.js 现有体式格局一致(用后缀 .node.json.js来辨别加载范例)。除非 Unambiguous JavaScript Grammar 的完成题目都处理掉,不然终究设计就是它了。
    • import() 人人都以为没题目,稳步推动中。
    • 因为 ES Module 的静态特征,之前给 CJS 模块做动态 Mock、MonkeyPatch 的体式格局都不可了。不过处理方法也有,一是在加载阶段供应钩子,二是许可对已加载的模块做热替代。
  • 2017.02.12
    Node.js CTC 和 TC39 的议论:

    • 因为 ES6 模块的异步特征,require() 将没法加载 ES6 模块。
    • Babel 现在支撑的 import { foo } from 'node-cjs-module' 也不符合范例,想 import 一个 NCJS 模块的话只能 import m from 'node-cjs-module' 然后 m.foo() 挪用。
    • .mjs 是题目起码的挑选。
    • (伤心的音讯来了)就现在盈余的事情内容预计,间隔 ES6 Module 终究完成约莫另有最少一年的时候(往好的一面想,终究看得到 timeline 了)。
  • 2017.05.10
    bmeck 在 Twitter 示意已完成了 .mjs 加载器的原型,在 Node.js v9 中可以用 flag 的体式格局启用,(愿望)在 v10 中正式推出。也就是另有一年的时候,一切顺遂的话 2018 年 4 月就可以看到 ES Module 正式到场 Node.js LTS。
  • 2017.05.11
    东西链对 .mjs 后缀的支撑都在推动中:

  • 2018.03.30
    Node.js 项目中和 ES Module 完成相干的 Issue 和 PR
  • 2018.04.25
    Node.js 10.0.0 宣布,到场了对 ES Module 的实验性支撑(须要 --experimental-modules 开启)
    https://github.com/nodejs/nod…
  • 2019.03.28
    新版 ES Module 设想定案,PR 合进骨干(https://github.com/nodejs/nod…),特征有变,依然运用 --experimental-modules 开启。现在的设计是遇上 4 月份 Node.js 12 宣布,终究在 2019 年 10 月进入 LTS。

参考资料

    原文作者:Amio
    原文地址: https://segmentfault.com/a/1190000004940294
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞