JS模块化加载方式 ,AMD、CMD、CommonJS、ES6四者间的异同
- AMD----- 依赖前置 (require.js)
引入模块-----require([ ],callback)
定义模块----- define([],callback)
不论定义模块,还是引入模块时,都会先行引入依赖,再定义/引入模块
- CMD ----- 依赖就近引入原则(seaJs)
引入模块 (遵循就近原则)
define(function (requie, exports, module) {
//依赖可以就近书写
var a = require('./a');
a.test();
});
定义模块----- define([],callback)
- CommonJS
引入模块-------require('模块路径')
定义模块 ------ exports.模块名= function(){ //some code ... }
ES6
引入模块 ----- import
定义模块 ----- export、export default
2、四种模块化加载方式的详说
2.1 AMD
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
步骤如下:
1、引入require.js,并用data-main指定入口的js文件。
2、在入口的js文件内用require.config配制各个模块的路径。
3、define定义js模块文件
4、require引入js模块文件
注意:
require([ ],callback)首先异步加载数组内的模块,随后才会去执行callback;
index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
<!--data-main属性的作用是,指定网页程序的主模块。在下面例中,
就是js目录下面的main.js,这个文件会第一个被require.js加载。
由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。-->
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.js" defer async="true" data-main="js/main"></script>
</html>
js/main.js (使用require引入模块)
js
//require配制各个模块加载路径
require.config({
// baseUrl: "./",
paths: {
"jquery": "jquery.min",
"math":"math",
"getStyle":"getStyle"
}
});
//真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。
//require()函数接受两个参数。
//第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;
//第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。
//加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。
//require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;
//它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
require(['jquery','math','getStyle'], function ($,math,getStyle){
let total = math.add(109,23);
console.log(total);
getStyle.setRedBg('body','#dddddd');
// some code ......
});
2.2 CMD
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
CMD与AMD的区别:
对于依赖的模块,AMD是提前执行,CMD是延迟执行。
(不过RequireJS从2.0开始,也改成可以延迟执行)
AMD推崇依赖前置,CMD推崇依赖就近。
js
//AMD
define(['./a','./b'], function (a, b) {
//依赖一开始就写好
a.test();
b.test();
});
//CMD
define(function (requie, exports, module) {
//依赖可以就近书写
var a = require('./a');
a.test();
...
//软依赖
if (status) {
var b = requie('./b');
b.test();
}
});
2.3 CommonJs规范
在模块中,通过require()方法来引入外部的模块。
上下文提供了exports 对象导出当前模块的方法和变量, 并且它是唯一导出的出口。
在模块中还存在一个module对象,它代表模块自身,而exports是module的属性。
案例如下:
math.js 定义导出模块
exports.math = function(){
//some code ...
}
另一个文件 引入模块
var math = require('math')1
2.4 ES6的import、export、export default
注意:
同一个文件中,可以有多个export,但只能有一个export default;
更多请参考:https://blog.csdn.net/yiyueqinghui/article/details/84382229
3、ES6 Module 和 CommonJS 模块的区别:
1、语法上
CommonJS 使用的是 module.exports = {} 导出一个模块对象,require(‘file_path’) 引入模块对象;
ES6使用的是 export 导出指定数据, import 引入具体数据。
2、CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6 Modules 的运行机制与 CommonJS 不一样。JS遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。因此,原始值变了,import加载的值也会跟着变。
3、CommonJS 全量加载整个模块,ES6可以选择性加载模块的部分内容
名称 | AMD | CMD | CommonJs | ES6 |
---|---|---|---|---|
全称 | Asynchronous Module Definition | Common Module Definition | CommonJs | ECMAScript6.0 |
同步异步 | 异步 | 异步 | 同步 | 同步/异步 均支持,取决于采用什么loader API |
实现实例 | RequireJS | SeaJS(淘宝) | nodeJS | JavaScript |
函数定义与引入 | 见代码块-01 | 见代码块-02 | 见代码块-03 | 见代码块-04 |
运行环境 | 浏览器 | 浏览器 | 服务端 | 前后端 |
代码块
- 代码块-01:AMD函数定义方式
// 声明
define(['moduel'], () => {
'use strict';
const name = 'jsong';
const sayHello = function() {
console.log(`hello ${name}`);
};
return { name, sayHello };
});
// 引入
require(['module'], module => {
module.sayHello();
});
- 代码块-02:CMD函数定义方式
// 声明
define(function(require, exports, module) {
const name = 'jsong';
const age = 11;
const sayHello = () => {
console.log(`hello ${name}`);
};
module.exports = { name, sayHello };
console.log(module.exports);
});
// 引入
define(function(require, exports, module) {
const m = require('./cmddefined');
m.sayHello();
});
- 代码块-03:CommonJs函数定义方式
// 声明
// 方式1
const name = 'jsong';
const age = 1;
const sayHelloName = function() {
console.log(name);
};
module.exports = { name, age, sayHelloName };
// { name: 'jsong', age: 1, sayHelloName: [Function: sayHelloName] }
console.log(module.exports);
// 方式2
exports.name = 'jsong';
exports.age = 11;
// {name:'jsong',age:11}
console.log(module.exports);
// 引入
const commonjs = require('./common.js');
const commonjs2 = require('./common2.js');
// jsong
commonjs.sayHelloName();
// jsong
console.log(commonjs2.name);
- 代码块-04:ES6函数定义方式
// 导出变量
export const name = 'jsong';
// 等价于
// const name = 'jsong';
// export { name };
export const sayHello = () => console.log(`hello ${name}`);
// 等价于
// const sayHello = () => console.log(`hello ${name}`);
// export { sayHello };
// 导出函数
export function eat() {
console.log(`${name} eat`);
}
// 等价于
// function eat() {
// console.log(`${name} eat`);
// }
// export { eat };
const age = 11;
// 导出对象
export const people = {
name,
age,
hello: () => console.log(`hello ${name}`)
};
// 等价于
// const people = {
// name,
// age,
// hello: () => console.log(`hello ${name}`)
// };
// export { people };
// 导出类
export class jsong{
sayHello() {
console.log(`hello ${name}`);
}
}
// 等价于
// class jsong {
// sayHello() {
// console.log(`hello ${name}`);
// }
// }
// export { jsong };
// 引入
// 例1
import * as people from './es6.js';
// jsong
console.log(people.name);
// hello jsong
people.sayHello();
// jsong eat
people.eat();
console.log(people.people);
// hello jsong
people.people.hello();
// hello jsong
new people.jsong().sayHello();
// 例2
import { name, sayHello } from './es6.js';
// jsong
console.log(name);
// hello jsong
sayHello();