2017-05-06

[译] 浏览器中的 ES6 module 实现

原文作者: @Jake Archibald
原文地址: https://jakearchibald.com/2017/es-modules-in-browsers/
译文地址: http://www.wemlion.com/post/es-modules-in-browsers
本文由 文蔺 翻译,转载请保留此声明。
著作权属于原作者,本译文仅用于学习、研究和交流目的,请勿用于商业目的。

ES6 的模块特性(module) 开始在浏览器端实现啦!一切正在路上...

浏览器备注
Safari 10.1(无)
Chrome Canary 60打开 启用“实验性网络平台功能”
Firefox 54打开 启用
Edge 15打开 启用“实验性 JavaScript 功能”

Live demo

只需为 元素添加 属性,浏览器就会把该元素对应的内联脚本或外部脚本当成 ECMAScript 模块进行处理。

目前已经有一些 很棒的关于 ECMAScript 模块的文章了,不过我还是想分享一些和浏览器相关的东西,它们都是我在测试代码、阅读规范的过程中学习到的。

尚未得到支持的 import 路径符号

有效的路径符号应当符合以下条件规则之一:

  • 完整的非相对路径。这样在将其传给的时候才不会报错。
  • 开头。
  • 开头。
  • 开头。

其他形式的符号被保留下来,未来将用于其他功能(如引入[import]内置模块)。

使用 属性向后兼容

Live demo

支持 的浏览器将会忽略带有 属性的 标签。这意味着我们可以为支持模块的浏览器提供模块形式的代码,同时为那些不支持模块的浏览器提供降级处理。

浏览器 issue

  • Firefox 暂不支持 (issue)。
  • Edge 暂不支持 (issue)。
  • Safari 10.1 暂不支持 ,但在最新的技术预览版中已经解决了此问题。对于 10.1 来说,有一个相当棒的解决方案

默认延迟执行

Live demo。脚本执行顺序为 , ,

获取脚本会导致 HTML parser 阻塞,这简直太太太太恶心了。对正常的脚本,我们可以使用 属性来防止阻塞,脚本将延迟至文档解析完毕后执行,同时保持与其他使用 的脚本之间的执行顺序。模块脚本的默认行为与 相同 —— 无法使模块脚本阻塞 HTML parser。

模块脚本与使用 的正常脚本使用相同的执行队列。

内联脚本同样延迟

Live demo。执行顺序依次为 、内联脚本、内联模块、

正常的内联脚本会忽略 属性,而内联模块则总是延迟执行,无论是否引入其他内容。

对内联、外部模块同样适用

Live demo。先完成加载的脚本先执行。

与正常脚本相同,带有 属性的脚本在下载时不会阻塞 HTML parser,一旦加载完毕,立即执行。不同的是, 对内联模块也同样适用。

使用 时,脚本的执行顺序可能会和它们在 DOM 中出现的顺序不尽相同。

浏览器 issue

  • Firefox 不支持内联模块使用 (issue)。

模块只执行一次

Live demo

引入同一个模块多次的时候,模块只会执行一次。这对 HTML 中的模块脚本同样适用 —— 在同一个页面中,URL 相同的模块只会执行一次。

浏览器 issue

  • Edge 会执行多次 (issue)。

总是使用 CORS

Live demo

与正常脚本不同,模块脚本(及其引入的脚本)是通过 CORS 获取的。这意味着,跨域模块脚本必须返回类似 这样的有效的响应头。

浏览器 issue

  • Firefox 无法加载 demo 页面(issue)。
  • Edge 加载了没有 CORS 响应头的模块(issue)。

不携带凭证信息

Live demo

在请求同源的情况下,多数基于 CORS 的 API 都会发送凭证信息(credentials,如 Cookie)。但 和模块脚本恰恰例外 —— 除非手动声明,否则是不会发送相关凭证的。

对于一个同源的模块脚本,可以为其添加 属性(这看起来挺怪的,我已经在规范中提出这个问题了),这样在请求时就可以携带相关凭证了。如果你还想将凭证发给其他域,请使用 。需要注意的是,接收凭证的域必须返回 的响应头。

此外,还有一个与“模块只会执行一次”这条规则相关的问题。浏览器是通过 URL 来区别不同模块的,所以如果你先请求了一个模块而不携带任何凭证,紧接着又携带凭证信息去请求该模块,那么第二次得到的依然是不携带凭证的请求所返回的模块。这正是我在上面代码中的 URL 后面加上“?”的原因。

浏览器 issue

  • 请求同源模块时,Chrome 会携带凭证信息(issue)。
  • 即使添加了 属性,Safari 在请求同源脚本时也不会携带凭证信息(issue)。
  • Edge 则完全弄反了。请求同源模块时,Edge 默认会发送凭证信息,但如果手动添加了 属性,则又不会携带 (issue)。

Firefox 是唯一正确实现这一点的浏览器 —— 好样的!

Mime-types

不同于普通脚本,对于通过 module 引入的脚本,服务器必须返回合法的 MIME type,否则脚本将不会执行。

Live demo

浏览器 issue

  • Edge 仍将执行 MIME type 非法的脚本(issue)。

以上就是我目前所学习到的所有内容。浏览器开始支持 ES6 模块,简直太开心啦~