目前比较主流的模块化规范有一下四种:
CommonJS(服务端)、AMD(外国人出品)、CMD(阿里出品)、ES6模块化(官方出品)。
模块化开发解决的问题:
- 污染全局变量
- js文件加载顺序
模块化进化历史:
阶段一:一个页面或者一个功能对应一个js文件
在一开始的时候,我们前端一般都会把一个页面的js写在同一个文件,把一个功能写在同一个js文件。在小项目上可能适用,但是在一个大项目中,就得考虑js文件的加载顺序了,因为js文件是从上往下加载的,而且这种方法,都是在全局上申明变量的,有可能造成全局变量污染,将一些数据给覆盖掉。
阶段二:插件化(闭包+自调用函数)
小例子:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body></body>
<script src="./a.js"></script>
<script src="./b.js"></script>
<script src="./index.js"></script>
</html>
a.js:
const moduleA = (()=>{
const a = [1,2,3]
return {
a
}
})()
b.js:
const moduleB = ((moduleA) => {
return {
b: moduleA.a.reverse()
}
})(moduleA)
index.js
;((moduleA, moduleB) => {
console.log(moduleA.a)
console.log(moduleB.b)
})(moduleA, moduleB)
问:moduleA和moduleB都是全局变量,为什么还要把它作为参数传进去?
答:因为这样可以把moduleA和moduleB做一个缓存。
总结:利用函数的作用域和闭包的,去避免全局变量污染,但是还是得去考虑js的加载顺序。
阶段三:服务端的CommonJS的出现
commonJS是一个模块化开发的规范,是在node.js中的模块化开发规范。
使用案例:
a.js:
const a = 1
module.exports = {
a
}
index.js:
const {a} = require('./a.js')
console.log(a) // 1
总结:node中会对加载的模块进行一个缓存,当第二次加载的时候就会大大的加快。
AMD的出现
AMD:(Asynchronous Module Definition)异步的模块定义。是国外的民间大佬写的一个模块化规范。AMD依靠requireJS来实现,所以如果想要用AMD的去开发,必须先引入require.js文件。
AMD的基本使用:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body></body>
<script src="./require.js"></script>
<script src="./index.js"></script>
</html>
a.js:
define('moduleA', function() {
const a = [1,2,3]
return {
a
}
})
b.js:
define('moduleB', ['moduleA'], function (moduleA) {
return {
b: moduleA.a.concat([5,6,7])
}
})
index.js:
require.config({
paths: {
moduleA: './js/a',
moduleB: './js/b'
}
})
require(['moduleA', 'moduleB'], function (moduleA, moduleB) {
console.log(moduleA.a) // 输出[1,2,3]
console.log(moduleB.b) // 输出[1,2,3,5,6,7]
})
AMD的define和require、require.config基础使用解释:
define:该方法用于定义模块,第一个参数是:你定义的模块名(string)。第二个参数(可选):你这个模块所要依赖的模块,是一个字符串类型的数组。第三个参数是:当加载完依赖后进行回调的function。
require: 该方法用于加载模块,第一个参数是:你需要加载的模块,是一个字符串类型的数组。第二个参数:和define方法的第三个参数一样,当加载完依赖进行回调的function。
require.config: 用于配置模块的路径。
require.config({
paths: {
moduleA: './js/a', // 路径,不用写.js,它会自动补充的。加了.js会报错
moduleB: './js/b'
}
})
CMD的出现
CMD:通用模块定义(common module define)。是我们阿里写的一个模块化规范。CMD依靠SeaJS去实现。如果你想使用CMD去模块化,就必须引入sea.js。
CMD的简单使用:
index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body></body>
<script src="./js/sea.js"></script>
<script src="./index.js"></script>
</html>
a.js:
define('moduleA', function(require, exports, module) {
const a = [1,2,3]
module.exports = {
a
}
})
b.js:
define('moduleB', function(require, exports, module) {
const moduleA = require('./a.js')
return {
b: moduleA.a.concat([4,5,6])
}
})
index.js
seajs.use(['moduleA', 'moduleB'], function(moduleA, moduleB) {
console.log(moduleA.a) // [1,2,3]
console.log(moduleB.b) // [1,2,3,4,5,6]
})
CMD中seajs.use和define这两个方法的基本使用说明:
seajs.use:使用模块的方法,第一个参数是一个字符串数组,表示需要用到的依赖。第二个参数是一个回调函数。
define: 用于定义模块的方法。第一个参数是模块id,第二个参数是一个回调函数,参数是:require、exports、module。这三个参数和CommonJS的用法一样,require用于加载模块,exports用于导出、module下的exports也可以导出,return也可以进行导出。
最终阶段ES6模块化:
ES6模块化:使用ECMAScript官方出,大家日常开发应该用的也比较多,下面就详细的介绍一下:
导入:
import module from '路径'
导出:
export default {}
export {}
各个规范之间的区别:
CommonJS和ES6模块化的区别:
- 由于浏览器对于es6的语法不能全面支持,所以ES6的模块是在编译的时候就进行了加载。而node环境是支持CommonJS的,所以CommonJS的模块是在运行的时候加载的。
- CommonJS导出的是一个拷贝的值,而且是一个浅拷贝。ES6导出的是引用。
AMD和CMD的区别:
- AMD是加载完所有的模块,才进性回调的。这种就依赖前置。
- CMD是需要的时候才去加载模块。这种叫做依赖就近、按需加载。
- AMD由于先加载所有模块才回调,所以用户体验良好。CMD由于是按需加载,所以性能良好。
CommonJS:有一种缓存机制,第一次的加载模块的时候,会把它缓存下来,下次就直接读取缓存就好了。