16.7 在浏览器中使用 ES6 模块

让我们看一下浏览器是如何支持 ES6 模块的。

浏览器对于 ES6 模块的支持工作正在进行

类似于模块加载,浏览器中对于模块其它方面的支持也正在进行中。所有你在此处读到的内容都可能改变。

16.7.1 浏览器:异步模块对比同步脚本

在浏览器中,有两种不同的实体:脚本和模块。它们有略微不同的语法,行为也不一样。

下面是一个不同点的概览,后面会详细讲述:

脚本 模块
HTML 元素 <script> <script type="module">
顶级的变量是 全局变量( global ) 模块的本地变量
顶级的 this window undefined
执行 同步地 异步地
声明式地引入( 引入语句 )
编程式地引入( 基于 Promise 的 API )
文件扩展名 .js .js

16.7.1.1 Script

Script 是浏览器中传统的嵌入 JavaScript 脚本和引用外部 JavaScript 文件的方式。 Script 有一个网络媒体类型属性,该属性用作:

  • web 服务器传回来的 JavaScript 文件的内容类型。
  • <script> 元素的 type 属性值。注意,对于 HTML5 ,如果 <script> 元素包含或者引用的是 JavaScript ,则推荐省略 type 属性。

下面是最重要的取值:

  • text/javascript :是一个遗留的值,如果省略 script 标签的 type 属性,默认值就是它。对于 Internet Explorer 8 和更早版本的浏览器来说,这是最安全的选择
  • application/javascript :现代浏览器中推荐使用

JavaScript 线程在代码加载并执行完之后停止。

16.7.1.2 模块

为了与 JavaScript 通常的运行完整性语义保持一致,模块体的执行不能被打断。这给导入模块留下了两个选择:

  • 1、在模块体执行的过程当中,同步加载指定的外部模块。这就是 Node.js 的方式。
  • 2、在模块体执行之前,异步地加载指定的所有模块。这就是 AMD 模块处理的方式,是浏览器环境的最佳选择,因为模块通过互联网加载,在模块加载的时候没有必要停止代码的执行。另一个好处是,这种方式允许并行地加载多个模块。

ECMAScript 6 提供了两种场景下都是最好的方式:Node.js 中的同步语法加上 AMD 的异步加载。 ES6 模块在语法上比 Node.js 模块简单:引入和导出必须要在顶级范围使用,这也意味着不能根据条件来引入。这种约束使 ES6 模块加载器能够静态分析当前模块引入了些什么模块,并在当前模块体执行之前加载好需要的模块。

脚本同步的本质使它们不能成为模块。脚本甚至不能声明式地引入模块(如果想引入模块,只能使用可动态编程编程引入的模块加载器 API )。

在浏览器中,模块可以通过新的 <script> 元素形式使用,这种方式是完全异步的:

<script type="module">
    import $ from 'lib/jquery';
    var x = 123;

    // The current scope is not global
    console.log('$' in window); // false
    console.log('x' in window); // false

    // `this` still refers to the global object
    console.log(this === window); // true
</script>

正如你看见的一样, script 元素有自己的作用域,在里面的变量是该作用域的本地变量。注意,模块代码默认是处于严格模式下的。这是一个好消息 - 不用再写 'use strict' 了。

类似于普通的 <script> 元素, <script> 也可用于加载外部的模块。例如,下面的标签通过 main 模块启动 web 应用( import 属性是我发明的,现在还不清楚会用什么名字)。

<script type="module" import="impl/main"></script>

在 HTML 中通过自定义 <script> 类型来支持模块的优点是:在老的浏览器中可以很容易地通过 polyfill (一种库)来引入这种支持。最终可能是也可能不是一个专用的模块元素(例如 module )。

16.7.2 打包

现代的 web 应用由很多通常比较小的模块组成。通过 HTTP 加载这些模块会带来性能上的负面影响,因为每一个模块都需要一个独立的请求。因此,在 web 开发世界里,将多个模块放在一个文件中是一种运用很久的传统做法。当前的方法很复杂,容易出错,并且仅对 JavaScript 有效。有两种解决办法:

  • HTTP/2 :将允许在一个 TCP 连接中执行多个请求,这使打包显得不是那么必要了。然后可以增量地更新应用,因为如果某一个模块改变了,浏览器没必要重新下载整个打包文件。
  • 包:另外, W3C 技术架构组正在制定一份用于“在 Web 环境上打包”的规范。想法是将整个文件夹放在一个包里面(想一下 ZIP 文件,但是是一种不同的格式)。然后这种包 URL :
http://example.org/downloads/editor.pack#url=/root.html;fragment=colophon

等价于下面这种普通的 URL ,如果这个包在服务器的根路径下打开的话:

http://example.org/root.html#colophon

浏览器必须确保一旦你在一个包里面,那么相对 URL 就要能按照预期工作。

本节来源

results matching ""

    No results matching ""