15 Likes
简析 AMD / CMD / UMD / CommonJS / ES Module

简析 AMD / CMD / UMD / CommonJS / ES Module

226 PV15 LikesJavaScriptJavaScript 模块
现代的前端项目一般都在采用 ES Module, 不过对于配置文件大多都在使用 CommonJS, 这边文章简析 AMD / CMD / CommonJS / ES Module 的发展史与异同.

CommonJS 和 ES Module 的区别

CommonJS 模块输出的是一个值的拷贝, ES6 模块输出的是值的引用

下面的例子, mod.incCounter() 不会影响 mod.counter, 对于基本数据类型, CommonJS 模块无论加载多少次, 都只会在第一次加载时运行一次, 以后再加载, 返回的都是第一次运行结果的缓存, 除非手动清除系统缓存.

// lib.js var counter = 3 function incCounter() { counter++ } module.exports = { counter: counter, incCounter: incCounter, } // main.js var mod = require('./lib') console.log(mod.counter) // 3 mod.incCounter() console.log(mod.counter) // 3

如果是个对象, 修改里面的值, 会影响原对象.

// lib.js var obj = { name: 'Yancey', age: 18 } function incCounter() { obj.name = 'Leo' } module.exports = { obj: obj, incCounter: incCounter, } // main.js var mod = require('./lib') console.log(mod.obj) // { name: 'Yancey', age: 18 } mod.incCounter() console.log(mod.obj) // { name: 'Leo', age: 18 }

如果把对象改成其他类型, 原变量不受响应.

// lib.js var obj = { name: 'Yancey', age: 18 } function incCounter() { obj = '沙雕' } module.exports = { obj: obj, incCounter: incCounter, } // main.js var mod = require('./lib') console.log(mod.obj) // { name: 'Yancey', age: 18 } mod.incCounter() console.log(mod.obj) // { name: 'Yancey', age: 18 }

JS 引擎对脚本静态分析的时候, 遇到模块加载命令 import 就会生成一个只读引用. 等到脚本真正执行的时候, 再根据这个只读引用到被加载的模块中取值. 因此, ES6 模块是动态引用, 并且不会缓存值, 模块里的变量绑定其所在的模块.

// lib.js export let counter = 3 export function incCounter() { counter++ } // main.js import { counter, incCounter } from './lib' console.log(counter) // 3 incCounter() console.log(counter) // 4

ES6 输入的模块变量只是一个"符号连接", 所以这个变量是只读的, 对它重新赋值会报错

// lib.js export let obj = {} // main.js import { obj } from './lib' obj.prop = 123 // 可以 obj = {} // 报错

CommonJS 是运行时加载, 这种特性不能使用 Tree-shaking; ES6 Module 是编译时输出接口, 可使用 Tree-shaking

const { stat, readFile } = require('fs') // 等价于 const _fs = require('fs') const stat = _fs.stat const readFile = _fs.readFile

AMD

使用 require.js

// 定义没有依赖的模块 define(function () { return 模块 }) // 定义有依赖的模块 define(['module1', 'module2'], function (m1, m2) { return 模块 }) //引入使用模块 require(['module1', 'module2'], function (m1, m2) { // 使用m1/m2 })

CMD

使用 sea.js

// 定义没有依赖的模块 define(function (require, exports, module) { exports.xxx = value module.exports = value }) // 定义有依赖的模块 define(function (require, exports, module) { // 引入依赖模块(同步) var module2 = require('./module2') // 引入依赖模块(异步) require.async('./module3', function (m3) {}) // 暴露模块 exports.xxx = value }) // 引入使用模块 define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show() })

CMD 和 AMD 的异同

  • 相同点: 都是异步加载

  • 不同点: AMD 依赖前置, 提前执行, js 可以方便知道依赖模块是谁, 立即加载; 而 CMD 就近依赖, 需要使用把模块变为字符串解析一遍才知道依赖了那些模块

UMD

所谓UMD (Universal Module Definition), 就是一种 JavaScript 通用模块定义规范, 让你的模块能在 JavaScript 所有运行环境中发挥作用.

[HTTP 系列] 第 3 篇 —— HTTP 缓存那些事

PREVIOUS POST

[HTTP 系列] 第 3 篇 —— HTTP 缓存那些事

聊一聊 ES6 的 Proxy 与 Reflect

NEXT POST

聊一聊 ES6 的 Proxy 与 Reflect

    Search by