浏览器中的ESModule
目前浏览器开始逐步支持原生 ESModule,那么如何理解这个特性呢?对于开发而言又会产生哪些变化呢? 本文参考自张鑫旭的博客
兼容性(2022-2-11)
目前主流浏览器版本均已支持
静态 import
当我们给 script 标签 加上 type="module"
时,浏览器就会自动把 內联或者外链的 script 认为是 ESModule .
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="module">
import testDefaultExport,{ testStaticImport } from './test.js'
testStaticImport()
testDefaultExport()
</script>
<script type="module" src="https://unpkg.com/react@17/umd/react.development.js" ></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
console.log(React);
});
</script>
<body>
浏览器es module 测试
</body>
</html>
js
export function testStaticImport() {
console.log("命名 export")
}
export default function testDefaultExport(){
console.log("默认 export")
}
注意: 如果希望使用 node 命名规范的 ESModule (mjs) 需要在服务器端设置 mine type 为 application/javascript
。
注意一些细节
对于老旧浏览器不支持 ESModule 的情况,可以使用
nomodule
进行兼容import 不支持裸露的说明符
javascript
// 不支持
import { A } from 'xxx.mjs';
import { B } from 'utils/xx.mjs';
// 支持
import React from 'https://unpkg.com/react@17/umd/react.development.js';
import { foo } from '/utils/bar.mjs';
import { foo } from './bar.mjs';
import { foo } from '../bar.mjs';
- 默认按 defer(defer 和 async 详情) 行为的顺序加载
- defer 简单说明
html
<!-- 同步 -->
<script src="1.js"></script>
<!-- 异步但顺序保证 -->
<script defer src="2.js"></script>
<script defer src="3.js"></script>
- module
html
<!-- module 默认外挂defer -->
<script type="module" src="1.mjs"></script>
<!-- 硬加载嘛 -->
<script src="2.js"></script>
<!-- 比第一个要晚一点 -->
<script defer src="3.js"></script>
<!-- 最终加载顺序 2,1,3 -->
- 内敛的 module
html
<!-- 默认 defer -->
<script type="module">
console.log('Inline module执行');
</script>
<!-- 硬加载 -->
<script src="1.js"></script>
<!-- 内敛 script 没有defer概念,因此写了没有用等于 硬加载 -->
<script defer>
console.log('Inline script执行');
</script>
<script defer src="2.js"></script>
<!-- 最终顺序 1.js -> Inline script -> Inline module ->2.js -->
- 支持
async
html
<!-- firstBlood模块一加载完就会执行 -->
<script async type="module">
import { pColor } from './firstBlood.mjs';
pColor('red');
</script>
<!-- doubleKill模块一加载完就会执行 -->
<script async type="module" src="./doubleKill.mjs"></script>
总是跨域 需要资源服务器端配置
Access-Control-Allow-Origin
字段天然无凭证 如果请求来自同一个源(域名一样),大多数基于 CORS 的 API 将发送凭证(如 cookie 等),但 fetch()和模块脚本是例外 – 除非您要求,否则它们不会发送凭证。
动态 import
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="module">
import("./test.js").then((module)=>{
console.log('动态引入')
module.default()
module.testStaticImport()
})
</script>
<body>
浏览器es module 测试
</body>
</html>
一点细节
import()
和静态 import
一样不能是裸露的地址。- 不同的是
import()
可以直接使用在 script 标签中 因此它没有type="module>"
的限制 import()
总是跨域