直接使用 <script>
标签加载
浏览器加载ES6模块,也可以使用 <script>
标签,但要加入 type="module"
属性。
浏览器对于带有 type="module"
的 <script>
,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等于打开了 <script>
标签的 defer
属性
如果网页有多个 <script type="module">
,它们会按照在页面出现的顺序依次执行。
<script>
标签的 async
属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。
一旦使用了async属性,<script type="module">
就不会按照在页面出现的顺序执行,而是只要该模块加载完成,就执行该模块。
静态import:
- 通过import以静态的方式,导入另一个通过export导出的模块(MDN)
示例
// ./utils.mjs
// 默认导出
export default () => {
console.log('来自 default export!');
};
// 命名导出 “doStuff”
export const doStuff = () => {
console.log('Doing stuff...');
};
静态引入和使用 ./utils.mjs
模块的方法
<script type="module">
import * as module from './utils.mjs';
module.default();
// -> 输出 "来自 default export!"
module.doStuff();
// -> 输出 "Doing stuff...'
</script>
Note:
示例里使用
.mjs
扩展名来表示它是模块而非常规脚本。
在Web上,文件扩展名并不重要,只要文件在 Content-Type
HTTP 标头中以正确的 MIME 类型(e.g. JS文件的 type/javascript)提供即可。
.mjs
扩展在其他平台很管用,比如 Node.js,因为它们没有 type="module"
来标识模块还是常规脚本。
所以这里使用同样的扩展来实现跨平台的一致性,并区分模块和常规脚本
这种用于导入模块的语法是一个静态声明:它只接受一个字符串文字作为模块说明符,并通过运行前(pre-runtime)"链接"过程将绑定引入本地作用域。
静态import导入语法只能在文件的顶部(top-level of the file)使用
静态import可以实现重要的用例,比如静态分析(static analysis)、bundling tools 和 tree-shaking
用法:
import defaultExport from "module-name";
import * as name from "module-name"; //导入整块模块的内容,name作为命名空间
import { export } from "module-name"; //导入单个导出
import { export as alias } from "module-name"; //导入时可以重命名导出
import { export1 , export2 } from "module-name"; //导入多个导出
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name"; //模块仅为副作用而导入,不导入模块中的任何内容。这将运行模块中的全局代码,但实际上不导入任何值
动态import():
Motivation
既然已有上一节的静态 import 可以实现模块导入,还需要动态 import 做什么呢?
动态 import 可实现的功能:
- 按需(或条件)导入模块
- 在运行时计算模块定位符(module specifier),(即 from 后面的 "module-name")
- 在常规脚本中引入一个模块
用法
动态 import(),引入了一种类似函数的新形式,可以在运行时动态加载 ES2015 模块:
var promise = import("module-name");
import(moduleSpecifier)
返回所请求模块的 promise,该 promise 是在 fetching、instantiating、以及 evaluating 所有模块依赖以及模块本身之后创建的。
<script type="module">
const moduleSpecifier = './utils.mjs';
import(moduleSpecifier)
.then((module) => {
module.default();
// -> 输出 "来自 default export!"
module.doStuff();
// -> 输出 "Doing stuff...'
});
</script>
这种使用方式也支持 await / async
关键字
let module = await import('/modules/my-module.js');
<script type="module">
(async () => {
const moduleSpecifier = './utils.mjs';
const module = await import(moduleSpecifier)
module.default();
// -> 输出 "来自 default export!"
module.doStuff();
// -> 输出 "Doing stuff...'
})();
</script>
Note:
如果从其他域名下(静态或动态)import scripts,则需要使用有效的 CORS 标头(比如
Access-Control-Allow-Origin:*
)返回脚本。
因为与常规脚本不同,模块脚本(和其引入)是使用CORS获取的
推荐用法:
将静态导入用于初始绘制依赖,尤其是对 above-the-fold 内容
在其他情况下,考虑用动态 import() 按需加载依赖
webpack note:
调用 import() 之处,被作为分离的模块起点,意思是:被请求的模块和它引用的所有子模块,会分离到一个单独的chunk中
import规范不允许控制模块的名称或其他属性。但是在webpack可以通过注释接收特殊参数
//单个目标
import(
/* webpackChunkName:"my-chunk-name" */
/* webpackMode:"lazy" */
'module'
);
//多个可能目标
import(
/* webpacklnclude:/\.json$/ */
/* webpackExc1ude:/\.noimport\.json$/ */
/* webpackChunkName:"my-chunk-name" */
/* webpackMode:"lazy" */
`./locale/${language}`
);
- webpackChunkName:
- 新chunk的名称。
- webpack 2.6.0开始,
[index]
和[request]
占位符,分别支持赋予一个递增的数字和实际解析的文件名
- webpackMode:
- 从webpack 2.6.0开始,可以指定以不同的模式解析动态导入。支持以下选项:
- "lazy":(默认)
- "lazy-once":
- "eager"
- "weak"
- 从webpack 2.6.0开始,可以指定以不同的模式解析动态导入。支持以下选项:
- webpackInclude:
- 在导入解析(import resolution)过程中,用户匹配的正则表达式。只有匹配到的模块才会被打包
- webpackExclude:
- 同上。所有匹配到的模块都不会被打包
webpackInclude
和 webpackExclude
选项不会影响到前缀,比如 ./locale
import 必须至少包含模块位于何处的路径信息。所以打包应当限制在一个指定目录或一组文件中
export()
默认导出整个模块,或具名导出模块(MDN)
//命名导出
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName() {...}
export class ClassName {...}
//默认导出。只能有一个默认的导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
export * from …; //不会导出已导入模块的默认导出
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
nameN:导出的标识符(用来被其他脚本的import导入)