Appearance
amd
amd Asynchronous Module Definition 异步模块化定义, requirejs实现了这套规范,思想是前置依赖,主要包括以下几个方法
- require.config() 配置依赖
- define([deps], callback)。定义模块
- require([deps], callback)。引用模块
js
// dependA.js
define([
'dependB'
], function(dependB) {
return {
start: function() {
document.write(dependB.name)
}
}
});
// dependB.js
define(function() {
return {
name: 'dependB'
}
});
// main.js
require.config({
baseurl: '/static/umd',
paths: {
"dependB": 'dependB',
"dependA": 'dependA',
}
})
require(['dependA'], function(dependA) {
console.log(dependA)
dependA.start()
})查看演示。requirejs 需要预先配置好所有依赖。不能动态获取所有
cmd
cmd 通用模块定义(Common Module Definition)。seajs实现了这套规范,依赖是就近依赖,延迟执行。
js
// main.js
seajs.config({
alias: {
dependB: './dependB.js'
dependA: './dependA.js'
}
})
seajs.use('dependA')
// dependA.js
define(function(require, exports, module) {
let dependB = require('dependB');
dependB.start()
})
// dependB.js
define(function(require, exports, module) {
'use strict';
exports.start = function() {
document.write('dependB')
}
});查看演示。seajs通过正则解析,预先将当前文件的require的module加载,所以require可以同步加载
commonjs
commonjs 是nodejs实现的一套模块化规范,和seajs类似。通过require、exports、module实现动态导入导出,不过不需要写define语句。
js
// moduleA.js
const a = require('A');
exports.someA = function() {
console.log('someA')
}commonjs下,为什么我们可以在文件中使用__dirname,__filename, require,module,exports等。 nodejs会将js文件进行包裹,类似下面这种。
js
(function(exports, require, module, __dirname, __filename,...) {
exports.say = function() {
console.log('say')
}
})包装函数如下
js
function wraper(functionStr) {
return `function(exports, require, module, __dirname, __filename) {
${functionStr}
}`
}
let moduleFunction = wraper`
export.say = function() {
console.log('say')
}
`
runInThisContext(moduleFunction)(exports, require, module, __dirname, __filename)runInThisContext 相当于eval或者new Function。实现动态申明函数的功能。读取文件内容 => 添加包裹 => eval生成函数 => 添加相应参数 => 执行,require整个流程。
在代码中可以打印require和module,查看具体详情。
require加载流程
require加载模块有3类
- nodejs底层核心模块
- 工程中代码
- npm 中的第三方代码
require标志符指require时的参数,按照以下方式解析
- 如果标识符是核心模块,加载核心模块。
- 如果标志符以
.//../开头,视为项目代码,会解析成绝对路径,作为module标志 - 如果不满足上面,则认为是第三方模块。
解析第三方模块规则如下
- 从当前__dirname 开始,寻找是否存在node_modules, 在node_module 中查找标识符目录,如果查询到,检查package.json 中是否存在main字段,如果有根据main字段指向的js作为module入口,如果没有使用index.js ,index.json ,index.node。
- 如果未找到node_modules, 在父级目录查找,直到根目录。重复执行上述流程。
!()[]
require 解析是按照深度优先的原则,按照代码执行顺序解析require。
require 简单源码
commomjs 将所有文件视为一个module,module包括文件解析相关信息。
js
function require(id) {
const cacheModule = Module._cache[id]
if(cacheModule) {
return cacheModule.exports
}
const module = {
exports: {},
loaded: false,
...
}
const fileContent = getFileContent(id);
Module._cache[id] = module;
runInThisContext(warper(fileContent))(module, module.exports, require, __filename, __dirname)
module.loaded = true;
return module.exports
}如上所示,相同标识符下,require只会加载一次,避免了循环引用问题。 delete require.cache[moduleName]; 具体参考 commonjs 详解
umd
umd是兼容amd和commonjs的规范,对代码做相应包括可实现。
js
(function (context, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
console.log('是commonjs模块规范,nodejs环境')
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
console.log('是AMD模块规范,如require.js')
define(factory)
} else if (typeof define === 'function' && define.cmd) {
console.log('是CMD模块规范,如sea.js')
define(function(require, exports, module) {
module.exports = factory()
})
} else {
console.log('没有模块环境,直接挂载在全局对象上')
root.umdModule = factory();
}
})(this, function(exports, module) {
// js 代码
})