(本文为个人笔记。欢迎转载,转载请注明出处。文章内容引用在本文底部。)
前言
模块化的开发方式可以提高代码复用率,方便进行代码的管理.。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有:CommonJS(node.js)、AMD、CMD (sea.js)以及ES6的模块系统。
CommonJS规范(node 模块 )
commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
硬盘 I/O | 网速 I/O |
---|---|
HDD: 100 MB/s | ADSL: 4 Mb/s |
SSD: 600 MB/s | 4G: 100 Mb/s |
SATA-III: 6000 Mb/s | Fiber: 100 Mb/s |
AMD规范 (RequireJS 对模块定义的规范化产出)
AMD规范采用异步方式加载模块,模块的加载不影响他后面语句的运行。
require.js实现AMD规范的模块化:用require.config()
指定引用路径等,用define()
定义模块,用require()
加载模块。
// AMD recommended style
define(["a", "b"], function(a, b){ // 依赖前置
a.doSomething();
b.doSomething();
})
CMD(SeaJS 对模块定义的规范化产出)
与AMD很类似,不同点在与:AMD推崇依赖前置、提前执行、CMD推崇依赖就近、延迟执行。
// CMD recommanded
define(function(require, exports, module){
var a = require("a");
a.doSomething();
var b = require("b");
b.doSomething(); // 依赖就近,延迟执行
})
ES6 Module
ES6在语言标准的层面上,实现了模块功能(官方)。旨在成为浏览器和服务器的通用模块解决方案。
其模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
export写法
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
export default
/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
ele.textContent = math.add(99 + math.basicNum);
}
ES6的模块不是对象,import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
ES6 模块与 CommonJS 模块的差异
1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
- ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
--------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
----------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
2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
-
运行时加载:CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
-
编译时加载:ES6 模块不是对象,而是通过
export
命令显式指定输出的代码,import
时采用静态命令的形式。即在import
时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
插件兼容
开发插件时,为了兼容不同规范,可以用一下代码:
;(function(){
function MyModule() {
// ...
}
var moduleName = MyModule;
if (typeof module !== 'undefined' && typeof exports === 'object' && define.cmd) { //cmd
module.exports = moduleName;
} else if (typeof define === 'function' && define.amd) { // amd
define(function() { return moduleName; });
} else { //others
this.moduleName = moduleName;
}
}).call(function() {
return this || (typeof window !== 'undefined' ? window : global);
});
参考及引用地址 (感谢作者)
http://huangxuan.me/js-module-7day/#/
https://juejin.im/post/5aaa37c8f265da23945f365c
http://es6.ruanyifeng.com/#docs/module-loader
https://www.jianshu.com/p/58fb2dafa584