一、 模块规范
一句话总结:CommonJs用于服务端Node.js中,AMD和CMD是针对客户端的规范,为了不让引入依赖阻塞了代码进行,使用了异步加载模块。
1. CommonJs:
- 适用区域:用于服务端。
- 导入:
require
- 导出:
module.exports
,exports
。 - exports与module.exports
exports
是module.exports
的引用,var exports = module.exports = {};
,而最终导出的是仍然是module.exports
,因此,倘若更改exports
的指向,最终返回的仍然是module.exports
。 - 用法:
module.exports 基础用法
/* export.js */
const a = 1
module.exports = a
/* import.js */
var a = require('./export')
// a = 1
exports 基础用法
/* export.js */
exports.a = 1 // 不能改变引用, exports = {a:1}🙅
/* import.js */
var a = require('./export')
// a = {a:1}
可以使用es6的特性 - 解构赋值
/* export.js */
exports.a = 1
/* import.js */
var {a} = require('./export')
// a = 1
- 为什么不适用于客户端?
同步引用不适用于客户端场景
var _ = require('./loadsh')
_.map([1,2,3], square)
如上面👆所示,如果loadsh这个库由于网络原因加载得很慢的话,那么它便阻塞了后面代码的运行,这样对用户体验牺牲很大。
这时候AMD就有了诞生的需求了。
2. AMD:
AMD是”Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
- 使用define定义模块:
// id:模块名(可选),dependencies:依赖库(可选),factory工厂方法,返回一个模版函数
define(id?, [dependencies?], factory)
// demo
define(['Lib'], function(Lib){
function foo(){
Lib.doSomething();
}
return {
foo : foo
};
});
- 使用require加载模块,因为是回调执行,所以不阻塞啦。
// module:加载的模块数组, callback:加载成功后再执行的回调函数
require([module], callback);
// demo
require([loadsh], loadsh.map([1,2,3],square))
目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js。
3. CMD:
CMD (Common Module Definition), 是seajs推崇的规范。它与AMD最大的区别是它推崇依赖就近。
- 同样,CMD使用define定义模块
// id:模块名(可选),dependencies:依赖库(可选),factory工厂方法,返回一个模版函数
define(id?, [dependencies?], factory)
// 不同点:它可以在工厂函数中导出导入(也是通常的做法),也是它推崇的导出导入方式
define(function(require, exports, module) {
// 模块代码
var $ = require('jquery.js')
$('div').addClass('active');
});
- 用use加载模块
// 加载模块
seajs.use(['myModule.js'], function(my){
});
- 与AMD的区别:
最明显的区别就是在模块定义时对依赖的处理不同
1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
这种区别各有优劣,只是语法上的差距,而且requireJS和SeaJS都支持对方的写法
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同。
AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行
CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的
二. ES6
这里主要介绍:
- ES6的import和export和export default的基本用法
- ES6和CommonJs的混用
1. 基本用法
/* export.js */
export const a = 1
/* import.js */
import { a } from './export'
// a = 1
经过babel的转换。可以看到转化为exports->require,并且给exports添加了一个__esModule的属性。
/* export.js */
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.a = void 0;
const a = 1;
exports.a = a;
/* import.js */
"use strict";
var _export = require("./export");
console.log(_export.a);
export default
// export.js
const b = 2
export default b
// import.js
import b from './export'
console.log(b)
通过babel转化,使用exports.default进行导出。
/* export.js */
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const b = 2;
var _default = b;
exports.default = _default;
⚠️注意:不能认为 import { b } from './export'
是用了解构赋值。当执行以下代码是不行的。import 语句中的"解构赋值"并不是解构赋值,而是 named imports。
// import.js
import { {a} } from './export' // 语法错误
console.log(a)
// export.js
export const b = { a: 1 }
named imports
,语法上和解构赋值很像,但还是有所差别,比如下面的例子。
import { b as a } from './export' // 解构赋值中不能用 as
因此还是得老老实实把值接下来,然后再去处理。或者你可以和CommonJs混用来达到类似解构赋值的效果。
2. 与CommonJs混用
由于babel解析的时候也是解析成exports->require形式的,因此可以和commonJs混用。
- import.js / require - export.js / export
// import.js
var { b } = require('./export').default
console.log(b)
// export.js
export default {
b: 2
}
- import.js / import - export.js / exports
// import.js
import { a } from './export'
console.log(a)
// export.js
module.exports = {
a: 1
}
网友评论