捣腾了一晚上,发现在 浏览器 实现 ES6 module 必须,使用 firefox54 以上和 chrome60 以上,并且 chrome 仍然有问题,跨域报错,得放在服务器下,firefox 可以实现。
ES6 的模块特性(module) 开始在浏览器端实现啦!一切正在路上...
浏览器 | 备注 |
---|---|
Safari 10.1 | (无) |
Chrome Canary 60 | 打开 chrome:flags 启用 "实验性网络平台功能" |
Firefox 54 | 打开 about:config 启用 dom.moduleScripts.enabled |
Edge 15 | 打开 about:flags 启用 " 实验性 Java Script 功能 " |
- `<script type="module">`
- import {addTextToBody} from './utils.js';
- addTextToBody('Modules are pretty cool.');
- </script>
- // utils.js
- export function addTextToBody(text) {
- const p = document.createElement('p');
- p.textContent = text;
- document.body.appendChild(p);
- }
pve demo。
只需为 script 元素添加 type=module 属性,浏览器就会把该元素对应的内联脚本或外部脚本当成 ECMAScript 模块进行处理。
目前已经有一些很棒的关于 ECMAScript 模块的文章了,不过我还是想分享一些和浏览器相关的东西,它们都是我在测试代码、 阅读 规范的过程中学习到的。
- // 支持
- 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';
有效的路径符号应当符合以下条件规则之一:
完整的非相对路径。这样在将其传给 new URL(moduleSpecifier) 的时候才不会报错。
以 / 开头。
以./ 开头。
以../ 开头。
其他形式的符号被保留下来,未来将用于其他功能(如引入 [import] 内置模块)。
- ` < script type = "module"src = "module.js" > ` < /script>
- `<script nomodule src="fallback.js">`</script >
pve demo。
支持 type=module 的浏览器将会忽略带有 nomodule 属性的 script 标签。这意味着我们可以为支持模块的浏览器提供模块形式的代码,同时为那些不支持模块的浏览器提供降级处理。
Firefox 暂不支持 nomodule(issue)。
Edge 暂不支持 nomodule(issue)。
Safari 10.1 暂不支持 nomodule,但在最新的技术预览版中已经解决了此问题。对于 10.1 来说,有一个相当棒的解决方案。
- `<script type="module" src="1.js">`</script>
- `<script src="2.js">`</script>
- `<script defer src="3.js">`</script>
pve demo。脚本执行顺序为 2.js,1.js,3.js。
获取脚本会导致 html parser 阻塞,这简直太太太太恶心了。对正常的脚本,我们可以使用 defer 属性来防止阻塞,脚本将延迟至文档解析完毕后执行,同时保持与其他使用 defer 的脚本之间的执行顺序。模块脚本的默认行为与 defer 相同 —— 无法使模块脚本阻塞 HTML parser。
模块脚本与使用 defer 的正常脚本使用相同的执行队列。
- `<script type="module">`
- addTextToBody("Inpne module executed");
- </script>
- `<script src="1.js">`</script>
- `<script defer>`
- addTextToBody("Inpne script executed");
- </script>
- `<script defer src="2.js">`</script>
pve demo。执行顺序依次为 1.js、内联脚本、内联模块、2.js。
正常的内联脚本会忽略 defer 属性,而内联模块则总是延迟执行,无论是否引入其他内容。
- ``<script async type="module">``
- import {addTextToBody} from './utils.js';
- addTextToBody('Inpne module executed.');
- </script>
- ``<script async type="module" src="1.js">``</script>
pve demo。先完成加载的脚本先执行。
与正常脚本相同,带有 async 属性的脚本在下载时不会阻塞 HTML parser,一旦加载完毕,立即执行。不同的是,async 对内联模块也同样适用。
使用 async 时,脚本的执行顺序可能会和它们在 DOM 中出现的顺序不尽相同。
Firefox 不支持内联模块使用 async(issue)。
- `<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>
pve demo。
引入同一个模块多次的时候,模块只会执行一次。这对 HTML 中的模块脚本同样适用 —— 在同一个页面中,URL 相同的模块只会执行一次。
Edge 会执行多次 (issue)。
- ` < script type = "module"src = "https://&helpp;.now.sh/no-cors" > ` < /script>
- `<script type="module">`
- import 'https:/ / &helpp;.now.sh / no - cors ';
- addTextToBody("This will not execute.");
- </script>
- `<script type="module" src="https://&helpp;.now.sh/cors">`</script>'
pve demo。
与正常脚本不同,模块脚本(及其引入的脚本)是通过 CORS 获取的。这意味着,跨域模块脚本必须返回类似 Access-Control-Allow-Origin: * 这样的有效的响应头。
Firefox 无法加载 demo 页面 (issue)。
Edge 加载了没有 CORS 响应头的模块 (issue)。
- ` < 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>
pve demo。
在请求同源的情况下,多数基于 CORS 的 API 都会发送凭证信息(credentials,如 Cookie)。但 fetch() 和模块脚本恰恰例外 —— 除非手动声明,否则是不会发送相关凭证的。
对于一个同源的模块脚本,可以为其添加 crossorigin 属性(这看起来挺怪的,我已经在规范中提出这个问题了),这样在请求时就可以携带相关凭证了。如果你还想将凭证发给其他域,请使用 crossorigin="use-credentials"。需要注意的是,接收凭证的域必须返回 Access-Control-Allow-Credentials: true 的响应头。
此外,还有一个与 "模块只会执行一次" 这条规则相关的问题。浏览器是通过 URL 来区别不同模块的,所以如果你先请求了一个模块而不携带任何凭证,紧接着又携带凭证信息去请求该模块,那么第二次得到的依然是不携带凭证的请求所返回的模块。这正是我在上面代码中的 URL 后面加上 "?" 的原因。
请求同源模块时,Chrome 会携带凭证信息 (issue)。
即使添加了 crossorigin 属性,Safari 在请求同源脚本时也不会携带凭证信息 (issue)。
Edge 则完全弄反了。请求同源模块时,Edge 默认会发送凭证信息,但如果手动添加了 crossorigin 属性,则又不会携带 (issue)。
Firefox 是唯一正确实现这一点的浏览器 —— 好样的!
不同于普通脚本,对于通过 module 引入的脚本,服务器必须返回合法的 MIME type,否则脚本将不会执行。
pve demo。
Edge 仍将执行 MIME type 非法的脚本 (issue)。
来源: https://www.2cto.com/kf/201712/704861.html