继续接上一篇来分享剩下的seajs源码,计划这一篇写完以后再来一篇就结束了。这一篇就是分析moudle之前的部分
首先是前面的一些变量声明
1 var doc = document 2 var cwd = dirname(doc.URL) 3 var scripts = doc.scripts 4 5 // 默认的是seajsnode作为id添加到dom中,或者作为script里最后一个项添加到dom中 6 var loaderScript = doc.getElementById("seajsnode") || 7 scripts[scripts.length - 1] 8 9 // 如果seajs正在工作时,取得正在执行的script的路径,或者取得cwd 10 var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd) 11 12 function getScriptAbsoluteSrc(node) { 13 return node.hasAttribute ? // IE6/7下没有 14 node.src : 15 // 详细属性见 http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx 16 node.getAttribute("src", 4) 17 } 18 19 20 // 开发测试用 21 seajs.resolve = id2Uri
下面这段开始是在util-request.js中
1 var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement 2 var baseElement = head.getElementsByTagName("base")[0] 3 4 var IS_CSS_RE = /\.css(?:\?|$)/i 5 var currentlyAddingScript 6 var interactiveScript
由于onload在webkit小于535.23和firefox小于9.0版本下是不支持onload事件的,所以要做一些额外处理。
1 var isOldWebKit = +navigator.userAgent 2 .replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+)\..*/, "$1") < 536
isOldWebkit 是用来确定webkit的版本号是否过于陈旧,这样用这个变量就可以额外在最后onload事件设置中做一些额外处理。
接下去的源码如下:
1 function request(url, callback, charset) { 2 var isCSS = IS_CSS_RE.test(url) 3 var node = doc.createElement(isCSS ? "link" : "script") 4 5 if (charset) { 6 var cs = isFunction(charset) ? charset(url) : charset 7 if (cs) { 8 node.charset = cs 9 } 10 } 11 12 // 添加onload 13 addOnload(node, callback, isCSS, url) 14 15 // 根据添加类型不同,增加不同属性 16 if (isCSS) { 17 node.rel = "stylesheet" 18 node.href = url 19 } 20 else { 21 node.async = true 22 node.src = url 23 } 24 25 // 在IE6-8中,在insert以后script会立刻执行 26 // 所以用currentlyAddingScript来维持住,用于在define中取得url 27 28 // ref: #185 & http://dev.jquery.com/ticket/2709 29 baseElement ? 30 head.insertBefore(node, baseElement) : 31 head.appendChild(node) 32 33 currentlyAddingScript = null 34 }
上面讲到的addOnload函数在下面分析,其中有node=null,这是考虑到IE下的变量销毁机制而设置的,可以有效减少内存使用
function addOnload(node, callback, isCSS, url) { var supportOnload = "onload" in node // 在webkit和firefox的老版本中 if (isCSS && (isOldWebKit || !supportOnload)) { setTimeout(function() { pollCss(node, callback) }, 1) // 在node加入之后就开始执行 return } if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) // 触发error自定义事件 onload() } } else { // 不支持onload的情况下,借助onreadystatechange node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } } function onload() { // 确保只运行一次,防止内存泄露 node.onload = node.onerror = node.onreadystatechange = null // 移除script if (!isCSS && !data.debug) { head.removeChild(node) } node = null callback() } }
对于是css的情况,通过检测sheet来辨别是否成功加载
function pollCss(node, callback) { var sheet = node.sheet var isLoaded // 对于WebKit < 536 if (isOldWebKit) { if (sheet) { isLoaded = true } } else if (sheet) { try { if (sheet.cssRules) { isLoaded = true } } catch (ex) { // 在 Firefox 13.0 以后`ex.name`的值从 "NS_ERROR_DOM_SECURITY_ERR" // 变为 "SecurityError" 但是我们这里只要测试小于9.0版本,所以不要紧 if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") { isLoaded = true } } } setTimeout(function() { if (isLoaded) { // 在这里执行callback 让css有够的时间运行 callback() } else { pollCss(node, callback) } }, 20) }
关于下面的require的正则匹配,太长了。。。没有好好读,待我研究一番
// 取得正在操作的script function getCurrentScript() { if (currentlyAddingScript) { return currentlyAddingScript } // 在 IE6-9 的浏览器中,onload事件可能不会正确运行 // 但是,我们可以认为,在script node中属性是interactive的状态时,就是正在运行的script // interactive在前面有声明,是seajs闭包中的全局变量 if (interactiveScript && interactiveScript.readyState === "interactive") { return interactiveScript } var scripts = head.getElementsByTagName("script") for (var i = scripts.length - 1; i >= 0; i--) { var script = scripts[i] if (script.readyState === "interactive") { interactiveScript = script return interactiveScript } } } // 测试用 seajs.request = request /** * util-deps.js - The parser for dependencies */ // require的正则判断 var REQUIRE_RE = /"(?:\\"|[^"])*"|‘(?:\\‘|[^‘])*‘|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["‘])(.+?)\1\s*\)/g var SLASH_RE = /\\\\/g function parseDependencies(code) { var ret = [] // 首先搞定多个\连在一起的情况,接下来就是require的匹配 code.replace(SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { ret.push(m2) } }) return ret }
后面三到四天要出去旅游了(毕业旅行呦!),回来以后把最后一篇补上。最后一篇计划是写完上面类似的分析后,整理一个运行的步骤,不然会感觉好混乱好混乱。。。。
呦,出去踏青喽!