前端模块化规范对比总结
用最简洁的方式了解模块化规范的特点
为什么要模块化,它解决了什么问题?
- 解决了命名冲突:每个模块独立,有自己的作用域
- 提高可维护性:将代码拆分,利于维护
- 性能优化:如异步加载
- 模块版本管理
主流模块化标准
- CommonJS
- AMD
- CMD
- UMD
- ES6
CommonJS
实现:NodeJS
使用module.exports暴露变量,函数
// num.js
var a = 1
var b = 2
var add = function (){
return a + b
}
// 导出
module.exports.a = a
module.exports.b = b
module.exports.add = add
var num = require('./num.js')
console.log(num.a) // 1
console.log(num.b) // 2
console.log(num.add(a,b)) // 3
CommonJS特点
- 每个文件都是一个独立的模块,有独立作用域,不会污染全局作用域
- 模块可被重复引用,但只会在第一次加载时运行一次,此后加载就直接读取缓存结果。要重新运行模块必须清除缓存
- modules.exports输出的是值的拷贝,一旦值被除数,模块内再发生并不爱不影响已经输出的值
- 同步加载,只有引入的模块加载完成,才会执行后面的操作。在服务端问题不大,但在浏览器中不合适
AMD规范
实现:require.js
专门为浏览器环境设计,异步加载模块
语法如下:
define(id?: String, dependencies?: String[], factory: Function|Object)
复制代码
id即模块的名字,字符串,可选dependencies指定了所要依赖的模块列表,它是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入factory中。如果没有指定dependencies,那么它的默认值是["require", "exports", "module"]factory包裹了模块的具体实现,可为函数或对象,如果是函数,返回值就是模块的输出接口或者值
导入
const foo = require("./xxx")
导出
module.exports = { ... }
AMD特点
-
异步加载,加载完成后可以执行回调
-
动态创建
script标签 -
推崇
依赖前置原则- 在模块代码逻辑开始前就把所有依赖前置执行,然后再执行自己的代码逻辑
CMD规范
代表:sea.js
define( function(require, exports, module){
...
})
复制代码
三个参数分别是:
-
require:可以引用其他模块,也可以用 require.async 异步调用其他模块 -
expxort:是一个对象,定义模块的时候,需要通过参数 export 添加属性来导出 API -
module:是一个对象,它有三个上属性- uri: 模块完整的 URI 路径
- dependencies:模块的依赖
- exports:模块需要被导出的 API
看个栗子
define( function(require, export, module){
const add = require("math").add
exports.increment = function(val){
return add(val, 1)
}
module.id = "increment"
})
CMD特点
-
同时支持同步和异步加载模块
-
懒加载
-
推崇
依赖就近原则- 即
在需要的时候才加载,如果require引入了依赖,但整个逻辑并未使用这个依赖或未执行到逻辑使用它的地方,依赖不会执行
- 即
UMD规范
UMD特点
- 可以通过运行时或者编译时让同一个代码模块在使用
CommonJs、CMD甚至是AMD的项目中运行 - 在定义模块的时候会检测当前使用环境和模块的定义方式,如果匹配就使用其规范语法,全部不匹配则挂载再全局对象上
ES Module
ES6发布的,在语言标准的层面上实现了模块功能
ES Module特点
-
在编译时就能确定模块的依赖关系,所有的加载都是引用
- 好处是可以执行静态分析和类型检查
Tree-Shaking就是在静态分析阶段移除掉JS上下文中没有引用的代码
-
在浏览器使用ES模块化
- 在 script 标签中添加
type="module"属性
- 在 script 标签中添加
-
Node.js从9.0版本开始支持ES模块
ES Module 和 CommonJS对比
| ES Module | Common JS |
|---|---|
import 静态引入 | require 动态引入 |
| 是对模块的引入,输出的是值的应用,改变原来模块中的值引用的值也会改变 | 对模块的拷贝,修改原来模块的值不会影响引用值 |
this指向undefined | this指向模块本身 |
| 在编译时确定依赖关系 | 运行时加载模块 |
| 可以单独加载某个方法 | 加载整个模块 |
| 不能被重新赋值 | 可以重新赋值(改变this指向) |
参考