requ(path){
// 找到路径,引进代码字符串
// let code = load(path)
let code = 'function add(a, b) {return a+b}; module.exports = add'
code = `(function(module){${code}})(context)`
// 此处context就是内部的module
let context = {}
const run = new Function('context',code)
run(context)
return context.exports
}
在浏览器环境,所有的 Js 模块都需要远程读取,尴尬的是,受限于浏览器提供的能力,并不能通过 ajax 以文件流的形式将远程的 js 文件直接读取为字符串代码。前提条件无法达成,上述运行策略便行不通,只能另辟蹊径
这就是为什么有了 CommonJs 规范了,为什么还会出现 AMD/CMD 规范的原因
那么浏览器上是怎么做的呢?在浏览器中通过 Js 控制动态的载入一个远程的 Js 模块文件,需要动态的插入一个 <script> 节点:
// 摘抄自 require.js 的一段代码
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);
要知道,设置了 <script> 标签的 src 之后,代码一旦下载完成,就会立即执行,根本由不得你再封装成闭包,所以文件模块需要在定义之初就要做文章,这就是我们说熟知的 AMD/CMD 规范中的 define,开篇的 add.js 需要重新改写一下
// add.js 文件
define ('add',function () {
function add (a, b) {
return a + b;
}
return add;
})
而对于 define 的实现,最重要的就是将 callback 的执行结果注册到 context 的一个模块数组中:
context.modules = {}
function define(name, callback) {
context.modules[name] = callback && callback()
}
AMD 和 CMD 的差异主要是,前者需要在定义之初声明所有的依赖,后者可以在任意时机动态引入模块。CMD 更接近于 CommonJS
两种规范都需要从远程网络中载入模块,不同之处在于,前者是预加载,后者是延迟加载