1.模块化的理解
模块有两个核心要素:隐藏和暴露
隐藏的,是自己的内部实现
暴露的,是希望外部使用的接口
在node.js中,由于只有一个入口文件,而开发一个应用肯定会涉及到多个文件的配合,因此,node.js对模块化的需求比浏览器端要大
2.模块化的规范
1.CommonJS
适用于服务器端的模块化,如果要用在浏览器端,需要借助browserify将app.js文件打包编译
CommonJS使用exports导出模块,require导入模块
nodejs中导入模块,使用相对路径,并且必须以./
或../
开头
nodejs对CommonJS的实现
为了实现CommonJS规范,nodejs对模块做出了以下处理
- 1.为了保证高效的函数运行,仅加载必要的模块。nodejs只有执行到require函数时才会加载并执行模块
- 2.为了隐藏模块中不被导出的代码,nodejs执行模块时,会将模块中的所有代码放在一个函数中执行,以保证不污染全局变量
- 3.为了保证顺利导出模块的内容,nodejs做了以下处理
1)在模块开始执行前,初始化一个值module.exports = {}
2)module.exports = {}
即模块的导出值
3)为了方便开发者便捷的导出,node在初始化完module.exports
后,又声明了一个变量exports = module.exports
- 4为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
(function (module) {
module.exports = {};
var exports = module.exports;
// 模块中的代码
return module.exports;
})();
两种导出方式
方法一
一个文件中可以导出多个
exports.getNumber = getNumber;
exports.abc = 123;
var count = 0;
function getNumber() {
count++;
return count;
}
// exports = {}相当于一个对象
exports.getNumber = getNumber;
/**
* exports={
* getNumber: getNumber
* }
*/
方法二
一个文件中只能导出一个,相当于导出全部内容
/** 定义模块 example.js **/
var x = 5;
var addX = function(value){
return value + x;
}
module.exports.x = x;
module.exports.addX = addX;
总结:其实两者本质上没啥区别
导入
// app.js
/** 引用模块 **/
var example = require('./example')
console.log(example.x)
console.log(example.addX(1))
CommonJS的加载机制
2.AMD
是异步模块加载机制
require.js实现了AMD规范,使用时需要引用require.js
在浏览器端使用
<script data-main="./index.js" type="./require.js"></script>
index.js是入口文件
define([依赖的模块列表], function(模块名称列表) {
// 模块内部的代码
return 导出的内容
})
在AMD中,导入和导出模块的代码,都必须放置在define函数中
导出
// a.js
define(function() {
// 模块内部的代码
var a = 1;
var b = 2;
return {
name: "a模块",
data: "a模块的数据"
}
})
导入
/**
define([依赖的模块列表], function(模块名称列表) {
// 模块内部的代码
return 导出的内容
})
*/
define(["a"], function(a) {
// 模块内部的代码,等a.js加载完后再运行
console.log(a)
})
3.CMD
公共模块定义规范
sea.js实现了CMD规范
<script type="./sea.js"></script>
<script>
seajs.use("./index.js")
</script>
在CMD中,导入和导出模块的代码,都必须放在define函数中
define(function(require, exports, modules) {
// 模块内部的代码
})
例子:
define(function(require, exports, modules) {
var a = require("a")
// 模块内部的代码
module.exports = "a模块的内容"
})
4.ES6模块化
export命令用于规定模块的对外接口,import命令用于输入其他模块的功能
分为基本导出和默认导出
基本导出
export var a = 1
或者
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
默认导出
每个模块,除了允许有多个基本导出外,还允许有一个默认导出
默认导出类似于CommonJS中的module.exports,由于只有一个,因此无需具名
export default 默认导出的数据
导入
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
导入默认导出和基本导出
data是默认导出,a和b是基本导出
import data, {a, b} from "a.js"
总结
- CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
- AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。
- CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
- ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。