likes
comments
collection
share

【ES6】module基本用法

作者站长头像
站长
· 阅读数 8

1.概述

在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJSAMD两种。前者用于服务器,后者用于浏览器。【只能在运行时确定】ES6 module是在浏览器和服务器上的通用解决方案。

CommonJS

模块就是对象,输入时必须查找对象属性。

 // CommonJS模块
 let { stat, exists, readfile } = require('fs');
 ​
 // 等同于
 let _fs = require('fs');
 let stat = _fs.stat;
 let exists = _fs.exists;
 let readfile = _fs.readfile;

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。

这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6 module

是通过export命令显式指定输出的代码,再通过import命令输入。

设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

 // ES6模块
 import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。

这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

模块功能主要由两个命令构成:exportimport

  • export命令用于规定模块的对外接口;
  • import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

2.模块加载

所有的模块都会像<script defer>①【延迟推迟执行脚本】加载的脚本一样按顺序执行。

也可以给模块标签添加async属性【异步②】,这样模块的执行顺序不再与<script>标签顺序绑定,也不会等待文档完成解析才执行。

但是入口模块仍需等待其依赖加载完成。

模块标签代码分为两种类型:

  • 嵌入模块代码:<script type="module"></scrip>(只适合做入口模块
  • 外部加载JS模块:<script type="module" src="module.js"></scrip>

一个页面上的入口模块没有数量限制,也可以重复,但实际上一个模块只会在一个页面被加载一次。

浏览器支持:

  • ES6模块既可以通过浏览器原生加载,也可以与第三方加载器和构建工具一起加载。
  • 完全支持ES6模块的浏览器可以从顶级模块加载整个依赖图,异步递归按需加载

模块行为:

  • 只在加载后执行
  • 只能加载一次
  • 单例
  • 可以定义公共接口,其他模块可以基于这个公共接口观察和交互
  • 可以请求加载其他模块
  • 支持循环依赖

新行为:

  • 默认在严格模式下执行
  • 不共享全局命名空间
  • 模块顶级thisundefined(一般是window
  • var声明不会添加到window对象
  • 异步加载

[①]  解析到标签后立即下载模块文件,但执行会延迟到文档解析完成。

[②]  解析到标签后进行异步下载,下载成功后立马执行。

3.模块导出export

导出语句必须在模块顶级,不能嵌套再某个语句块(如条件语句if)中。

一般来说,声明、赋值和导出标识符最好分开,同时也让export语句集中在一块。

错误形式:

  • 行内默认导出不能出现变量声明
  • 只有标识符可以出现在export子句,不能是123这种数值
  • 别名只能出现在export子句中

命名导出 name export

出现次数没有限制,顺序也没有限制(但是尽量先声明后导出)

 export const foo = "foo"
 const foo = "foo"
 const bar = "bar"
 export {foo}
 export {foo as MyFoo}   // 别名
 export {foo as default} // 别名为default则为默认导出
 export {foo. bar as MyBar}  // 可以同时部分或全部导出,可以分别命名别名

默认导出 default export

default关键字将一个值声明为默认导出,每个模块只能有一个默认导出。

默认导出和命名导出不冲突,可以组合为一行export { foo as default, bar}

4.模块导入import

导入语句必须在模块顶级,不能嵌套再某个语句块(如条件语句if)中。

import语句会被提升到模块顶部,所以语句顺序不重要,但是推荐放在模块顶部

模块标识符可以是相对路径、绝对路径【必须是纯字符串,不能是计算结果】

在浏览器中通过标识符原生加载模块,则文件必须带有 .js扩展名

导入对模块而言是只读的。(不能直接修改导出值,但是可以修改对象的属性)

可以同时使用以下两种导出方式来获取

命名导出

可以使用*批量获取并命名别名import * as Foo from '../xxx/xx'

也可以花括号{}指名导入import {foo, bar} from './foo.js'

默认导出

就好像整个模块就是导出的值一样

  • 可以不使用花括号{}
  • 可以使用default关键字并提供别名导入import { default as foo } from './foo.js'

参考资料

es6.ruanyifeng.com/#docs/modul…

JavaScript高级程序设计(第4版)第26章 模块