seajs 源码分析二

继续接上一篇来分享剩下的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 = /"(?:\"|[^"])*"|'(?:\'|[^'])*'|/*[Ss]*?*/|/(?:\/|[^/
])+/(?=[^/])|//.*|.s*require|(?:^|[^$])requires*(s*(["'])(.+?)1s*)/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
}

后面三到四天要出去旅游了(毕业旅行呦!),回来以后把最后一篇补上。最后一篇计划是写完上面类似的分析后,整理一个运行的步骤,不然会感觉好混乱好混乱。。。。

呦,出去踏青喽!

原文地址:https://www.cnblogs.com/cyITtech/p/3606217.html