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
兩種規范都需要從遠程網絡中載入模塊,不同之處在于,前者是預加載,后者是延遲加載