分类
主要介绍以下几种模块化方案
- ES6
- CommonJS
- AMD
ES6
介绍:ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
特点:
- 尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量,
- ES6 模块不是对象,而是通过
export
命令显式指定输出的代码,再通过import
命令输入,定义了输出的对外接口 - 这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象
- 编译时加载,使得静态分析成为可能
语法规则
- 另外,
export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值 -
export
命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错 - 在
export
命令后面,使用大括号指定所要输出的一组变量(包括变量,匿名函数,具名函数)看下这里错误的例子,变量输出类似
// 报错
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
看看 import
-
import
命令输入的变量都是只读的,因为它的本质是输入接口, 但是导入对象即使可以修改属性,也是不建议的 - 一般会把 import 语句放在处理的方法前,不过
import
命令具有提升效果,会提升到整个模块的头部,首先执行,也不会导致报错
foo();
import {foo} from 'my_module';
3.import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构
4. 通过 Babel 转码,CommonJS 模块的require
命令和 ES6 模块的 import
命令,可以写在同一个模块里面,但是最好不要这样做。因为 import
在静态解析阶段执行,所以它是一个模块之中最早执行的
5. 实现模块整体加载
6. 使用 export default
指定默认输出,对应的import
语句不需要使用大括号
import 函数
- 支持动态加载模块,运行时处理模块是优点,但是动态加载不能丢
- 支持异步,做按需加载
- 此时就类似于
require
加载
看一段比较秀的
async function main() {const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main()
1. 防止异步阻塞
2.import 返回值解构
ES6 与 CommonJS 的差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
CommonJS
-
CommonJS 的一个模块,就是一个脚本文件。
require
命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象,该对象关注三个属性。- id: 模块名
- exports: 输出的接口
- loaded: 脚本是否执行完毕
所以再次 require 不会执行脚本,只会获取对应的结果和拷贝值
- 加载时执行
-
语法
-
module.exports = value
或exports.xxx = value
-
require(xxx)
, 如果是第三方模块,xxx 为模块名;如果是自定义模块,xxx 为模块文件路径
-
AMD
async 开头的词一般都有异步含义,node api 里比较多,在浏览器加载模块就是采用这种方案
- 使用 define 定义模块
- 使用 require 使用模块,处理逻辑异步进行
模块化文章参考 https://es6.ruanyifeng.com/#d…
一些问题
- 按需加载
import {stat, exists, readFile} from 'fs';
这里只加载需要模块,其它不加载,这种按需加载是如何实现的(指的是最后打包文件中不含非相关代码),在项目中有哪些按需加载的例子
-
defer
与async
的区别是 - 在 node.js 中使用 ES6 的模块化
-
如何处理循环加载
- ES6 会在编译时分析模块,相当于在执行上下文中分析 VO,在循环加载时候看定义方式,没有变量提升的一般会导致报错
-
对于 CommonJS 而言,循环加载的执行过程是:
- 加载别的模块,js 模块执行权变为新模块
- 循环过程不会对已经加载模块重复运行,只会获取缓存结果
- 顺便说一下 webpack 的会有循环打包吗