# AMD(Async Module Definition)
# 特点
- 异步加载
- 依赖前置
define(['a','b], function() { a.do(); b.do(); }); - 提前执行 define 声明依赖的模块式就加载并执行模块内的代码
# 规范化产出
RequireJS 对模块定义的规范化产出:
- define() 定义模块
- require() 加载模块
- require.config() 指定资源路径
# 实现方式
异步加载
// 创建script标签节点
req.createNode = function (config, moduleName, url) {
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
return node
}
在这段代码中我们可以看出, requirejs 导入模块的方式实际就是创建脚本标签,一切的模块都需要经过这个方法创建。
注:在 req.load 函数中调用 createNode,监听 script load 事件,同时考虑浏览器兼容问题,并设置 script 的 src、将 script 添加到页面, 加载成功后调用 context.onScriptLoad,onScriptLoad 再调用 completeLoad(详情请参考源码)
# 组织依赖关系
通过 defQueue、defQyeyeMap 来管理,name 对应的 deps 全部加载完成后,执行 callback。
- context.defQueue.push([name, deps, callback]);
- context.defQueueMap[name] = true;
# 核心思想
RequireJS 加载模块的核心思想是利用了动态加载脚本的异步性以及 onload 事件
- 在 HTML 中引入 < script> 标签是同步加载;
- 在脚本中动态加载是异步加载,且由于被加载的脚本在事件队列的后端,因此总是会在当前脚本之后执行;
- 使用 onload 和 onerror 事件可以监听脚本加载完成,以异步的事件来处理异步的事件
# 机制
- RequireJS 使用 head.appendChild()将每一个依赖加载为一个 script 标签。
- RequireJS 等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。
# 模拟模块化加载
const myRequire = (deps, callback) => {
//记录模块加载数量
let ready = 0
//创建脚本标签
function load(url) {
let script = document.createElement('script')
script.type = 'text/javascript'
script.async = true
script.src = url
return script
}
let nodes = []
for (let i = deps.length - 1; i >= 0; i--) {
nodes.push(load(deps[i]))
}
//加载脚本
for (let i = nodes.length - 1; i >= 0; i--) {
nodes[i].addEventListener(
'load',
() => {
ready++
//如果所有依赖脚本加载完成,则执行回调函数;
if (ready === nodes.length) {
callback()
}
},
false
)
document.head.appendChild(nodes[i])
}
}
myRequire(['module1.js', 'module2.js', 'module3.js'], () => {
console.log('ready!')
})
# 缓存策略
require()的缓存策略:node.js 会自动缓存经过 require 引入的文件,使得下次再引入不需要经过文件系统而是直接从缓存中读取。不过这种缓存方式是经过文件路径定位的,即使两个完全相同的文件,但是位于不同的路径下,会在缓存中维持两份。可以通过 console.log(require.cache)获取目前在缓存中的所有文件。
# exports 与 module.exports 区别
require 导出的内容是 module.exports 的指向的内存块内容,并不是 exports 的。简而言之,区分他们之间的区别就是 exports 只是 module.exports 的引用,辅助后者添加内容用的。