seajs 路径解析过程

简要说明

本文对Seajs 3.0.1 的部分源码(util-path.js) 进行学习,习得的体会。

重点是对Sea.js中路径解析的过程进行源码级的理解和探索,包括seajs.resolve的定义;id解析到文件路径的过程;seajs.config 中alias,paths,vars,map等的具体使用。

文件出处:util-paths.js


resolve 的定义


    function id2Uri(id, refUri) {
      if (!id) return ""

      id = parseAlias(id)
      id = parsePaths(id)
      id = parseAlias(id)
      id = parseVars(id)
      id = parseAlias(id)
      id = normalize(id)
      id = parseAlias(id)

      var uri = addBase(id, refUri)
      uri = parseAlias(uri)
      uri = parseMap(uri)

      return uri
    }

    // For Developers
    seajs.resolve = id2Uri

可以看到resolve解析id的主要过程:

alias, 别名解析
path, path解析
vars, 变量解析
normalize, 后缀标准化js
addBase, 添加基础路径
map 路径映射

至此 resolve 让id 变成 了文件的路径,可以进行后续的文件访问。


alias 别名

seajs.data.alias 存放的是seajs.config中设置的别名映射。

    function parseAlias(id) {
      var alias = data.alias
      return alias && isString(alias[id]) ? alias[id] : id
    }

alias 解析的函数中可以看到:id作为一个整体 作为alias映射的key


paths 路径

seajs.data.paths 存放的是seajs.config 中设置的paths的路径映射。

    //匹配路径正则表达式 
    var PATHS_RE = /^([^/:]+)(\/.+)$/;

    function parsePaths(id) {
      var paths = data.paths
      var m

      if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
        id = paths[m[1]] + m[2]
      }

      return id
    }

这里主要就是路径的正则表达式:id.match(PATHS_RE)

例如:

id  = "abc/def/ghi ";
id.match(PATHS_RE) // 结果是["abc/def/ghi", "abc","def/ghi"]  

这里取值 m[1] ,获取的是 第一个 / 之前的字符串作为 paths 的 key ,来获取 paths[‘abc’]的值

最后的结果是: paths[‘abc’] + ‘def/ghi’


vars 变量

seajs.data.vars 存放的是seajs.config 中设置的vars对象。

    // 匹配vars 的格式 {..}
    var VARS_RE = /{([^{]+)}/g

    function parseVars(id) {
      var vars = data.vars

      if (vars && id.indexOf("{") > -1) {
        id = id.replace(VARS_RE, function(m, key) {
          return isString(vars[key]) ? vars[key] : m
        })
      }

      return id
    }

如果 id 中存在{ , 将id中的{key} 替换为 vars中定义的vars[key]

主要在正则表达式匹配id中的{..} 格式的字符:

    vars: {
        "bbb" : "ccc"
    }
    id = "abc/{bbb}/def"

VARS_RE 匹配结果: [{bbb},bbb] , 其中子模式 bbb作为key,取得vars变量的值 vars[bbb],字符串替换,结果是 abc/ccc/def


normalize 标准化后缀

  1. #结尾的path,不处理
  2. .js 结尾,或者 包含有? 的path, 不处理
  3. / 结尾,不处理

给path 添加后缀.js

    function normalize(path) {
          var last = path.length - 1
          var lastC = path.charCodeAt(last)

      // If the uri ends with `#`, just return it without '#'
      if (lastC === 35 /* "#" */) {
        return path.substring(0, last)
      }

      return (path.substring(last - 2) === ".js" ||
          path.indexOf("?") > 0 ||
          lastC === 47 /* "/" */) ? path : path + ".js"
    }

addBase 添加base 基础路径

给解析后的path添加上基础路径,形成可以用给的文件地址
可以在seajs.config 中配置base来说设置基础 路径

1. 绝对路径   http://  或者 // 开头
2. 相对路径    ./  ../ 
3. 根路径     /
4. 顶级路径   a/b/c

上面四种路径的形式,解析成一个有效的文件地址

    // "//"开头 或者 包含":/" 的绝对路径
    var ABSOLUTE_RE = /^\/\/.|:\//

    // 匹配 ..//../ 结构的字符
    var ROOT_DIR_RE = /^.*?\/\/.*?\//

    function addBase(id, refUri) {
      var ret
      var first = id.charCodeAt(0)

      // Absolute  绝对路径
      if (ABSOLUTE_RE.test(id)) {
        ret = id
      }
      // Relative
      else if (first === 46 /* "." */) {
        ret = (refUri ? dirname(refUri) : data.cwd) + id
      }
      // Root
      else if (first === 47 /* "/" */) {
        var m = data.cwd.match(ROOT_DIR_RE)
        ret = m ? m[0] + id.substring(1) : id
      }
      // Top-level
      else {
        ret = data.base + id
      }

      // Add default protocol when uri begins with "//"
      if (ret.indexOf("//") === 0) {
        ret = location.protocol + ret
      }

      return realpath(ret)
    }

绝对路径: ABSOLUTE_RE 匹配的,直接返回
相对路径: 相对于参考路径或者当前工作目录
根路径: 当前工作目录如果是一个根路径,拼接一个地址
顶级路径: 前面加上 base

// 开头的path,添加上协议


realpath 真实路径

转换path为一个真实路径;其中包含对相对路径,多余// 的处理;从过程中可以看到正则表达式的强大。

var DOT_RE = /\/\.\//g
var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//
var MULTI_SLASH_RE = /([^:/])\/+\//g    

其中DOT_RE 匹配 /./ 模式的相对路径,当前目录下的文件,直接使用 /替换

a/./b => a/b

MULTI_SLASH_RE 将多个/ 转化 只有一个 /

a///b//c => a/b/c

DOUBLE_DOT_RE 在除去多余/ 后, 进行切换到上层目录,将上层目录替换为 /

a/b/../c  => a/c  其中/b/../ 是匹配的字符串,用/ 替换  

源码:

    // Canonicalize a path
    // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"

    function realpath(path) {
      // /a/b/./c/./d ==> /a/b/c/d
      path = path.replace(DOT_RE, "/")

      /*
        @author wh1100717
        a//b/c ==> a/b/c
        a///b/////c ==> a/b/c
        DOUBLE_DOT_RE matches a/b/c//../d path correctly only if replace // with / first
      */
      path = path.replace(MULTI_SLASH_RE, "$1/")

      // a/b/c/../../d  ==>  a/b/../d  ==>  a/d
      while (path.match(DOUBLE_DOT_RE)) {
        path = path.replace(DOUBLE_DOT_RE, "/")
      }

      return path
    }

map 映射

在对id解析后,得到一个文件地址,最后进行一次map映射。
seajs.config 可以设置map的映射规则,如果uri应用了一条映射规则后,便返回,只映射一次。

    function parseMap(uri) {
      var map = data.map
      var ret = uri

      if (map) {
        for (var i = 0, len = map.length; i < len; i++) {
          var rule = map[i]

          ret = isFunction(rule) ?
              (rule(uri) || uri) :
              uri.replace(rule[0], rule[1])

          // Only apply the first matched rule
          if (ret !== uri) break
        }
      }

      return ret
    }

总结

以上贴上了seajs的相关源码,最其中的思想是很佩服的。通过了解这些实现过程,对于我们使用seajs将会更加的便利。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值