在浏览器中高效使用JavaScript module(模块)

在浏览器中也可使用JavaScript modules(模块功能)了。目前支持这一特性的浏览器包括:javascript

  • Safari 10.1.
  • 谷歌浏览器(Canary 60) – 须要在chrome:flags里开启”实验性网络平台功能(Experimental Web Platform)”
  • Firefox 54 – 须要在about:config里开启dom.moduleScripts.enabled选项。
  • Edge 15 – 须要在about:flags里开启 Experimental JavaScript Features 选项
<script type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Modules are pretty cool.'); </script> 
// utils.js export function addTextToBody(text) { const div = document.createElement('div'); div.textContent = text; document.body.appendChild(div); } 

咱们须要作的只是在script标签元素上声明type=module就能够了,这样,浏览器就能解析代码中的module语法了。html

网上已经有不少关于modules的好文章了,但我在这里想单独分享一下专门针对浏览器里的ECMAScript modules的知识,这些都是我在阅读和测试ECMAScript规范时学到的。java

目前并不支持”裸” import 语法

// 能够这样使用: import {foo} from 'https://jakearchibald.com/utils/bar.js'; import {foo} from '/utils/bar.js'; import {foo} from './bar.js'; import {foo} from '../bar.js'; // 这些写法不支持: import {foo} from 'bar.js'; import {foo} from 'utils/bar.js'; 

下面几种 module 语法是有效的:git

  • 绝对路径的URL。也就是说,使用 new URL(模块地址) 也不会报错。
  • 地址开头是 /
  • 地址开头是 ./
  • 地址开头是 ../

另一些语法是保留为未来使用的,好比,导入内部的(built-in) modules。es6

用来保持向后兼容的nomodule

<script type="module" src="module.js"></script> <script nomodule src="fallback.js"></script> 

可以认识type=module语法的浏览器会忽略具备nomodule属性的scripts。也就是说,咱们可使用一些脚本服务于支持module语法的浏览器,同时提供一个nomodule的脚本用于哪些不支持module语法的浏览器,做为补救。github

浏览器问题

  • Firefox并不支持nomodule属性 (issue)。但在Firefox nightly版里已经修复了这个问题。
  • Edge并不支持nomodule属性。 (issue).
  • Safari 10.1 并不支持nomodule属性,但在最新的技术预览版中修复了这个问题。对于10.1版, 有一个很聪明的变通技巧。

缺省设置为Defer

<!-- 这个脚本的执行将会晚于… --> <script type="module" src="1.js"></script> <!-- …这个脚本… --> <script src="2.js"></script> <!-- …可是会先于这个脚本. --> <script defer src="3.js"></script> 

执行的顺序将会是2.js1.js3.js.web

若是script代码块阻止HTML分析器下载其余代码,这是很是糟糕的事情,一般咱们会使用 defer 属性来防止这种解析阻塞,但同时这样也会延迟script脚本的执行——直到真个文档解析完成。并且还要参考其它deferred script脚本的执行顺序。Module scripts缺省行为状态很像 defer 属性的做用 – 一个 module script 不会妨碍HTML分析器下载其它资源。chrome

Module scripts队列的执行顺序跟使用了defer属性的普通脚本队列的执行顺序同样。api

Inline scripts一样是deferred

<!-- 这个脚本的执行将会晚于… --> <script type="module"> addTextToBody("Inline module executed"); </script> <!-- …这个脚本… --> <script src="1.js"></script> <!-- …和这个脚本… --> <script defer> addTextToBody("Inline script executed"); </script> <!-- …但会先于这个脚本。--> <script defer src="2.js"></script> 

执行的顺序是 1.js, inline script, inline module, 2.js.跨域

普通的inline scripts会忽略defer属性,而 inline module scripts 永远是deferred的,无论它是否有 import 行为。

外部 & inline modules script脚本上的 Async 属性

<!-- 这个脚本将会在imports完成后当即执行 --> <script async type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Inline module executed.'); </script> <!-- 这个脚本将会在脚本加载和imports完成后当即执行 --> <script async type="module" src="1.js"></script> 

这个快速下载script会率先执行。

跟普通的scripts同样, async 属性能让script加载的同时并不阻碍HTML解析器的工做,并且在加载完成后当即执行。跟普通的scripts不一样的是, async 属性在inline modules脚本上也有效。

由于永远都是async, 因此这些scripts的执行顺序也许并不会像它们出如今DOM里的顺序。

浏览器问题

  • Firefox并不支持inline module scripts上的 async 特性。(issue).

Modules只执行一次

<!-- 1.js 只执行一次 --> <script type="module" src="1.js"></script> <script type="module" src="1.js"></script> <script type="module"> import "./1.js"; </script> <!-- 而普通的脚本会执行屡次 --> <script src="2.js"></script> <script src="2.js"></script> 

若是你知道 ES modules,你就应该知道,modules能够import屡次,但只会执行一次。这种原则一样适用于HTML里的script modules – 一个肯定的URL上的module script在一个页面上只会执行一次。

浏览器问题

  • Edge浏览器会屡次执行 modules (issue).

CORS 跨域资源共享限制

<!-- 这个脚本不会执行,由于跨域资源共享限制 --> <script type="module" src="https://….now.sh/no-cors"></script> <!-- 这个脚本不会执行,由于跨域资源共享限制--> <script type="module"> import 'https://….now.sh/no-cors'; addTextToBody("This will not execute."); </script> <!-- 这个没问题 --> <script type="module" src="https://….now.sh/cors"></script> 

跟普通的scripts不一样, module scripts (以及它们的 imports 行为) 受 CORS 跨域资源共享限制。也就是说,跨域的 module scripts 必须返回带有有效 Access-Control-Allow-Origin: * 的CORS头信息。

浏览器问题

  • Firefox没有成功的加载演示页面 (issue).
  • Edge浏览器加载了没有 CORS headers 的 module scripts (issue)。

没有凭证信息(credentials)

<!-- 有凭证信息 (cookies等) --> <script src="1.js"></script> <!-- 没有凭证信息 --> <script type="module" src="1.js"></script> <!-- 有凭证信息 --> <script type="module" crossorigin src="1.js?"></script> <!-- 没有凭证信息 --> <script type="module" crossorigin src="https://other-origin/1.js"></script> <!-- 有凭证信息--> <script type="module" crossorigin="use-credentials" src="https://other-origin/1.js?"></script> 

当请求在同一安全域下,大多数的CORS-based APIs都会发送凭证信息 (cookies 等),但 fetch() 和 module scripts 例外 – 它们并不发送凭证信息,除非咱们要求它们。

咱们能够经过添加crossorigin属性来让同源module脚本携带凭证信息,若是你也想让非同源module脚本也携带凭证信息,使用 crossorigin="use-credentials" 属性。须要注意的是,非同源脚本须要具备 Access-Control-Allow-Credentials: true 头信息。

一样,“Modules只执行一次”的规则也会影响到这一特征。URL是Modules的惟一标志,若是你先请求的Modules没有携带凭证信息,而后你第二次请求但愿携带凭证信息,你仍然会获得一个没有凭证信息的module。这就是为何上面的有个URL上我使用了一个?号,是让URL变的不一样。

浏览器问题

  • 谷歌浏览器在请求同源 modules 时带有 credentials (issue).
  • Safari 请求同源 modules 时不带有 credentials,即便你使用了 crossorigin 属性标志。 (issue).
  • Edge浏览器彻底相反。它缺省状态下对同源请求 modules 时带有 credentials 而当你指定了 crossorigin 属性后反倒没了。 (issue).

火狐浏览器是惟一作的正确的浏览器,干的漂亮!

Mime-types 文档类型

跟普通的scripts不同,modules scripts 必须指定一个有效的 JavaScript MIME 类型,不然将不会执行。

浏览器问题

  • Edge浏览器在无效MIME types下也执行 (issue).

这些就是目前我学到的。毋庸置疑,ES modules可以登录浏览器,已经让我兴奋不已了。你呢?

相关文章
相关标签/搜索