[译] 浏览器中的 ECMAScript 模块

栏目: JavaScript · 发布时间: 5年前

内容简介:原文链接:作者:Jake Archibald浏览器现在可以使用 ES 模块(module)了!它们是:

原文链接: ECMAScript modules in browsers

作者:Jake Archibald

浏览器现在可以使用 ES 模块(module)了!它们是:

  • Safari 10.1
  • Chrome 61
  • Firefox 60
  • Microsoft Edge 16
<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>
复制代码
// utils.mjs
export function addTextToBody(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}
复制代码

在线演示

您只需要在 script 元素上添加 type=module ,浏览器就会将内联脚本或外部脚本作为 ECMAScript module 处理。

关于模块(module)已经有一些很棒的文章,但是我想分享一些在我测试和阅读规范的时候学到的浏览器特有的内容。

目前还不支持的某些 import 用法

// 已支持:
import {foo} from 'https://jakearchibald.com/utils/bar.mjs';
import {foo} from '/utils/bar.mjs';
import {foo} from './bar.mjs';
import {foo} from '../bar.mjs';

// 不支持:
import {foo} from 'bar.mjs';
import {foo} from 'utils/bar.mjs';
复制代码

有效的模块路径说明符必须符合下列条件之一:

new URL(moduleSpecifier)
/
./
../

其他形式的说明符保留供将来使用,例如导入内置模块。

使用 nomodule 来向后兼容

<script type="module" src="module.mjs"></script>
<script nomodule src="fallback.js"></script>
复制代码

在线演示

理解 type=module 的浏览器会忽略属性为 nomodule 的脚本。这意味着您可以给支持模块的浏览器提供模块树,同时给其他浏览器提供一个回退版本。

浏览器问题

  • Firefox 浏览器不支持 nomodule ( issue ) 。已在 Firefox nightly 中修复!
  • Edge 浏览器不支持 nomodule ( issue ) 。已在 Edge 16 中修复!
  • Safari 浏览器不支持 nomodule 。已在 Safari 11 中修复!对于 10.1,这里有一个 非常聪明的替代办法

默认情况下推迟

<!-- 这个脚本的执行会晚于… -->
<script type="module" src="1.mjs"></script>

<!-- …这个脚本… -->
<script src="2.js"></script>

<!-- …但是会在这个脚本之前执行。 -->
<script defer src="3.js"></script>
复制代码

在线演示

执行的顺序是: 2.js1.mjs3.js

script 在获取期间会阻塞 HTML 解析器的方式是非常糟糕的。对于常规脚本,您可以使用 defer 来避免阻塞,当然这也会推迟脚本的执行,直到文档完成解析,并与其他延迟脚本一起维护执行顺序。模块脚本的默认表现行为就像 defer ——当它正在获取时,没有办法让一个模块脚本阻塞 HTML 解析器。

模块脚本使用和添加了 defer 的常规脚本相同的执行队列。

内联脚本也是延时的

<!-- 这个脚本的执行会晚于… -->
<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 ,内联脚本,内联脚本, 2.js

常规的内联脚本会忽略 defer ,然而内联模块脚本却总是被延迟,无论它们有没有导入任何东西。

异步适用于外部和内联模块

<!-- 一旦获取了导入,就会执行此操作 -->
<script async type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Inline module executed.');
</script>
<!-- 一旦获取了脚本和它的导入,就会执行此操作 -->
<script async type="module" src="1.mjs"></script>
复制代码

在线演示

快速下载的脚本会在慢速下载的脚本之前执行。

与常规脚本一样, async 会让脚本在下载过程中不会阻塞 HTML 解析器,并且尽快地执行。与常规脚本不同, async 也适用于内联模块。

与往常的 async 一样,脚本不会按照它们出现在 DOM 中的顺序执行。

浏览器问题

  • Firefox 浏览器不支持内联模块脚本上的 async ( issue ) 。已在 Firefox 59 中修复!

模块仅执行一次

<!-- 1.mjs 仅执行一次 -->
<script type="module" src="1.mjs"></script>
<script type="module" src="1.mjs"></script>
<script type="module">
  import "./1.mjs";
</script>

<!-- 然而,普通的脚本却执行多次 -->
<script src="2.js"></script>
<script src="2.js"></script>
复制代码

在线演示

如果您理解 ES 模块,您就会知道您虽然可以引入它们很多次,但是它们却仅仅会执行一次。当然,这同样适用于HTML中的脚本模块 - 特定URL的模块脚本每页只执行一次。

浏览器问题

  • Edge 执行多次模块 (issue)。已修复,但是还没发布(希望 Edge 17 会带上这个修复内容)。

总是 CORS

<!-- 该脚本不会执行, 因为它不能通过 CORS 检查 -->
<script type="module" src="https://….now.sh/no-cors"></script>

<!-- 该脚本不会执行, 因为它引入的脚本之一不能通过 CORS 检查 -->
<script type="module">
  import 'https://….now.sh/no-cors';

  addTextToBody("This will not execute.");
</script>

<!-- 该脚本会执行,因为它通过了 CORS 检查 -->
<script type="module" src="https://….now.sh/cors"></script>
复制代码

在线演示

与常规脚本不同,模块脚本(和它引入的内容)是通过 CORS 获取的。这就意味着跨域的模块脚本必须返回有效的 CORS header ,比如 Access-Control-Allow-Origin: *

浏览器问题

  • Firefox 加载 Demo 页面失败 (issue)
  • Edge 加载没有 CORS header 的模块脚本 (issue)。 已在 Edge 16 中修复!

没有凭据

<!-- 使用凭据获取(cookie 等) -->
<script src="1.js"></script>

<!-- 不使用凭据获取 -->
<script type="module" src="1.mjs"></script>

<!-- 使用凭据获取 -->
<script type="module" crossorigin src="1.mjs?"></script>

<!-- 不适用凭据获取 -->
<script type="module" crossorigin src="https://other-origin/1.mjs"></script>

<!-- 使用凭据获取 -->
<script type="module" crossorigin="use-credentials" src="https://other-origin/1.mjs?"></script>
复制代码

在线演示

如果请求来自相同的源,大多数基于 CORS 的 API 会发送凭据(cookie 等),但是 fetch() 和模块脚本却是例外的——非您要求它们,否则它们不会发送凭据除。

您可以通过添加 crossorigin 属性来向同源模块添加凭据(这对我来说似乎有点奇怪, 我在规范中对此提出质疑 )。如果您打算向其他的源也发送凭据,使用 crossorigin="use-credentials" 。注意其他源必须使用 Access-Control-Allow-Credentials:true 的 header 来响应。

此外,还有一个与“模块只执行一次”规则相关的问题。模块由其URL标记,因此如果您请求没有凭据的模块,然后使用凭据请求它,您将获得相同的无凭证模块。 这就是为啥我在上面的URL中使用 问号 ? 的原因,使它们成为唯一的。

更新:上面的情况可能很快就会发生改变。 fetch() 和模块脚本默认都会向同源的 URL 发送凭据。 Issue

浏览器问题

  • Chrome 使用凭据请求同源模块(issue) 。已在 Chrome 61 中修复!
  • Safari 即使添加了 crossorigin 属性,也不使用凭据请求同源模块(issue)。
  • Edge 即使添加了 crossorigin 属性,也不使用凭据请求同源模块(issue) 。已在 Edge 16 中修复!
  • Edge 使用凭据请求同源模块(issue)。

MIME 类型

不同于常规脚本,模块脚本必须是 有效的 JavaScript MIME 类型 中的一种类型,否则模块就不会执行。HTML 标准建议使用 text/javascript


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

实用Common Lisp编程

实用Common Lisp编程

Peter Seibel / 田春 / 人民邮电出版社 / 2011-10 / 89.00元

由塞贝尔编著的《实用Common Lisp编程》是一本不同寻常的Common Lisp入门书。《实用Common Lisp编程》首先从作者的学习经过及语言历史出发,随后用21个章节讲述了各种基础知识,主要包括:REPL及Common Lisp的各种实现、S-表达式、函数与变量、标准宏与自定义宏、数字与字符以及字符串、集合与向量、列表处理、文件与文件I/O处理、类、FORMAT格式、符号与包,等等。......一起来看看 《实用Common Lisp编程》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具