早期的JavaScript是没有模块的概念,引用第三方包时都是把变量直接绑定在全局环境。这导致变量污染(同名问题)和依赖混乱。
Node 应用由模块组成,采用 CommonJS 模块规范。
每个模块是个独立的 js 文件。
每个模块中 module 变量代表当前模块,模块的 exports 属性暴露对外的接口
node 为了简化书写,为每一个模块提供了一个 exports 变量指向 module.exports
不能把exports直接指向一个值,这样就相当于切断了 exports 和module.exports 的关系
require方法用于加载模块
相当于读入并执行一个js文件,然后返回该模块的 exports 对象
require命令用于加载文件,后缀名默认为.js,可以省去
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了
CommonJS 特点:
- 为模块提供单独的作用域
- 多次引入,只会在第一次执行,之后读取缓存
- 加载顺序为代码中出现顺序 (同步)
导入机制特点: 对于已导出的变量,不受原文件方法的影响 require的是被exports的值的拷贝
模块可以通过 events 模块发出事件;通过.on方法 监听模块发出的事件
require.resolve() 返回加载的确切文件名
require.main 判断模块是直接执行(值为module本身),还是被调用执行(false)
// 删除指定模块的缓存
delete require.cache[moduleName];
// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
delete require.cache[key];
})
循环加载,再次被加载的模块是不完全执行的,会停在 require 的位置
属性 | 解释 |
---|---|
id | 模块的识别符,通常是带有绝对路径的模块文件名 |
filename | 模块的文件名,带有绝对路径 |
loaded | 返回一个布尔值,表示模块是否已经完成加载 |
parent | 返回一个对象,表示调用该模块的模块 可以用于判断是否为入口脚本 |
children | 返回一个数组,表示该模块要用到的其他模块 |
exports | 表示模块对外输出的值 |
模块缓存机制 解决循环依赖问题
允许添加回调函数,异步加载模块 代表 require.js
使用define方法定义模块
define([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;
第二个参数callback,则是加载成功之后的回调函数。
让输出的模块兼容CommonJS规范
define(function (require, exports, module){
var someModule = require("someModule");
var anotherModule = require("anotherModule");
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
exports.asplode = function (){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
});
代表 Sea.js 保证依赖先于文件加载完成
参考博客
define 用来定义模块
define(name?,[depend]?,constructor)
name: 模块标识;
[depend]: 模块的依赖文件数组;
constructor: 构造函数 默认三个参数:require、exports、module
* 当此参数是字符串,对象时,返回的接口就是本身。
* 当为函数时,以这个函数为模块的构造函数,执行此函数,得到向外提供的接口。
if (typeof define === "function" && define.cmd) // 有满足CMD规范的模块加载器存在
require(name)
加载
require.async(name,callback?);
异步加载
详情链接
通过 “模块地图”和“模块记录” 解决循环依赖问题
已经进入过的模块标注为获取中,遇到import语句会去检查这个地图,已经标注为获取中的则不会进入,地图中的每一个节点是一个模块记录,上面有导出变量的内存地址,导入时会做一个连接——即指向同一块内存。