SeaJS 解析
原创
2018-3-14
16:18
编辑于
2022-6-17
15:28
源加载原理
使用 require 加载资源(JavaScript 和 CSS),背后是采用 link 和 script 来加载 CSS 和 JavaScript。
function request(url, callback, charset) {
var isCSS = IS_CSS_RE.test(url)
var node = doc.createElement(isCSS ? "link" : "script")
if (charset) {
var cs = isFunction(charset) ? charset(url) : charset
if (cs) {
node.charset = cs
}
}
addOnload(node, callback, isCSS, url)
if (isCSS) {
node.rel = "stylesheet"
node.href = url
}
else {
node.async = true
node.src = url
}
// For some cache cases in IE 6-8, the script executes IMMEDIATELY after
// the end of the insert execution, so use `currentlyAddingScript` to
// hold current node, for deriving url in `define` call
currentlyAddingScript = node
// ref: #185 & http://dev.jquery.com/ticket/2709
baseElement ?
head.insertBefore(node, baseElement) :
head.appendChild(node)
currentlyAddingScript = null
}
!> 需要注意的是,动态插入 script 标签加载 JS 的方式不会按照插入的顺序来执行。如果想要按插入顺序来执行,可以配置<script async="false">
,即采用阻塞的方式,但是这个存在浏览器兼容性问题。
require 加载机制
在 NodeJS 中,require 是执行到当前行是才去加载,但是在浏览器端,由于 script 加载无法中断当前执行的代码,相当于是“异步”的,因此无法模拟 NodeJS 中的执行方式。哪么实际是如何加载的呢?用下面的代码来测试一下。
define(function(require) {
debugger;
console.log("加载 a.js");
require("./a.js");
console.log("加载 b.js");
require("./b.js");
});
打开控制台,运行到断点时,查看 Network 面板,发现 a.js 和 b.js 都已经加载完了。那么 seajs 是如何做到提前加载 a.js 和 b.js 的呢?原理就是通过将 define 传入的函数转成字符串,然后正则匹配出 require 的资源,提前加载。源码如下:
Module.define = function (id, deps, factory) {
// ...
if (!isArray(deps) && isFunction(factory)) {
deps = parseDependencies(factory.toString())
}
// ...
}
var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g
var SLASH_RE = /\\\\/g
function parseDependencies(code) {
var ret = []
code.replace(SLASH_RE, "")
.replace(REQUIRE_RE, function(m, m1, m2) {
if (m2) {
ret.push(m2)
}
})
return ret
}
其中比较关键的就是匹配 require 的正则(REQUIRE_RE),其复杂度也说明了重要性。
// 去掉 REQUIRE_RE /g 用 match 查看匹配结果
'require("a.js");'.match(REQUIRE_RE)
// ["require("a.js")", """, "a.js"]
'require("a-b.min.js");'.match(REQUIRE_RE)
// ["require("a-b.min.js")", """, "a-b.min.js"]
'require("http://www.qinshenxue.com/a.js");'.match(REQUIRE_RE)
// ["require("http://www.qinshenxue.com/a.js")", """, "http://www.qinshenxue.com/a.js"]
加载流程
以下面的代码为例,用流程图的形式展示整个加载流程。
// main.js
define(function(require) {
require("./a.js");
require("./b.js");
});
// a.js
define(function(require) {
console.log("a.js");
});
// b.js
define(function(require) {
console.log("b.js");
});
转载请注明出处。本文地址:
https://www.qinshenxue.com/article/20180109085513.html

关注我的公众号