Javascript Modules

179 阅读2分钟

如何定义一个 js module

<script type="module" src="index.mjs"></script>

js module 特性(和classic script有啥区别)

  1. 默认开启 strict-mode
  2. 不支持html-style注释(原来classic script还支持这个…)
  3. Module有自己的作用域,在module执行 var foo = 1 不会在global上创建foo变量,window.foo也拿不到
  4. global this指向undefined
  5. 支持 import/export
  6. 支持 Top-level await · V8
  7. 默认开启defer模式
  8. 受跨域限制,拉取异域js module的时候,需要支持cors字段(比如Access-Control-Allow-Origin: *)
  9. 可以对inline script使用async关键字

为啥要用 js modules

现状

目前咱都用webpack/rollup/parcel这些打包工具,如果啥配置都不搞,一般都是打包成一个巨大的[hash].bundle.js。 这么搞有几个问题:

  1. 甭管用得到用不到的代码,都打到bundle里面了。 这种情况有缓存还好,第一次进来没缓存的用户得下一个非常的js文件,影响首屏速度
  2. 不利于缓存命中,一旦改了一行代码, [hash]会变化,导致无法命中缓存。

当然平时打包也不太可能就只打一个bundle,而常用的策略比如根据route来打包,每一个route打一个包,这样能拆的更细一些。

js modules

对应现状,用js modules有啥好处呢?如果咱们不打bundle,每个js文件作为一个的module,这样改a.js的代码就不会影响b.js的缓存。

同时,为了支持低端浏览器,咱们在打包的时候一般都会用像babel这样的工具做一次转译,把我们用的比较新的特性(比如常用的 async/await, import/export)转译成一坨低端浏览器看的懂的代码。 这么搞一遍之后,代码体积就大了,损耗性能。 其实对于支持js modules的浏览器来说,像async/await这种特性也一定支持了,没有必要再转译一遍。 所以通过type="module"引入的js代码都会比较精简,这也算一个好处。

当然,低端浏览器也不能就不管了。 像下面这样通过nomodule来做polyfill就好了

<script type="module" src="index.mjs"></script>
<script nomodule src="bundle.js"></script>

但是有大牛做过benchmark, 当应用比较大(>300个模块)的时候,用unbundle的js modules 性能其实要比 bundle的版本差。

原因大概有2个:

  1. bundle的时候其实是做了不少优化的,别人把依赖理清楚了,又是minimize又是tree shaking的,其实给我们删掉了不少无用代码。
  2. 拉一个module就是一个http请求,如果不用http2的话,加上浏览器的请求限制,高并发下性能会比较惨。

参考

JavaScript modules

Using Native JavaScript Modules in Production Today

Deploying ES2015+ Code in Production Today