前端模块化理解:
前端模块化就是复杂的文件变成一个个独立的模块,比如js文件等,分成独立的模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间互相依赖的问题,所有有了commonJS规范,AMD,CMD规范等等,以及用于js打包(编译处理)的工具webpack
说一下commonJS、AMD、CMD
一. commonJS
Node.js是commonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持module、exports、require、global。实际用时,用module.exports定义当前模块对外的接口(不推荐直接用exports),用require加载模块。
//定义math.js
var basicNum = 0;
function add(a,b){
return a+b;
}
module.exports = {
add:add,
basicNum:basicNum
}
//引用自定义模块时,参数包含路径
var math = require('./math')
math.add(2,3)
//引用核心模块时,不需要带路径
var http = require('http');
复制代码
commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不回有问题。但是在浏览器端,限于网络原因,更合理的方案是异步加载。
二.AMD和require.js
AMD规范采用异步加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。这里介绍用require.js实现AMD规范的模块化:用require.config()指引路径等,用define()定义模块,用require()加载模块。
三.CMD和sea.js
CMD是另一种js模块化方案,它于AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。次规范其实是在sea.js推广过程中产生的。
四.ES6 Module
ES6在语言标准的层面上,实现了模块功能化,而且实现的非常简单,旨在称为浏览器和服务器通用的模块化解决方案。其模块功能主要由两个命令构成:export和import。export命令用于规定模块对外接口,import命令用于入出其他模块提供的功能。
// 定义模块 math.js
var basicNum = 0;
var add = function(a,b){
return a+b;
}
//引用模块
import {basicNum,add} from './math'
function test(ele){
ele.textContent = add(99 + basicNum);
}
复制代码
如上例所示,使用import命令的时候,用户知道所要加载的变量名或函数名。ES6还提供了export default命令,为模块指定默认输出,对应的import语句不需要使用大括号。更趋近于AMD的引用写法。
//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模块是动态引用,并且不回缓存值,模块里面的变量绑定其所在的模块。
2.CommonJS模块是运行时加载,ES6模块是编译时输出接口
- 运行时加载:CommonJS模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称之为‘运行时加载’。
- 编译时加载:ES6模块不是对象,而是通过export命令显示指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称之为‘编译时加载’。
3.CommonJS导入的模块路径可以是一个表达式,因为它使用的是require()方法;而ES6 Modules只能是字符串
4.CommonJS的this指向当前模块,ES6 Modules的this指向undefined
注:目前浏览器对ES6 Module兼容性还不太好,我们平时在webpack中使用的export/import,会被打包为exports/require
六.AMD和CMD区别
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同,二者皆为异步加载模块。
- AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
- CMD推崇依赖就近,只有在用到某个模块的时候再去require
七.总结
- CommonJS用在服务器端,AMD和CMD用在浏览器环境
- AMD是RequireJS在推广过程中对模块定义的规范化产出
- CMD是SeaJS在推广过程中模块定义的规范化产出
- AMD提前执行(异步加载:依赖先执行)+延迟执行
- CMD延迟执行(运行到需要加载,根据顺序执行)