簡要說明
本文對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[]])) {
id = paths[m[]] + m[]
}
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("{") > -) {
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 标準化字尾
- 以 #結尾的path,不處理
- 以.js 結尾,或者 包含有? 的path, 不處理
- 以 / 結尾,不處理
給path 添加字尾.js
function normalize(path) {
var last = path.length -
var lastC = path.charCodeAt(last)
// If the uri ends with `#`, just return it without '#'
if (lastC === /* "#" */) {
return path.substring(, last)
}
return (path.substring(last - ) === ".js" ||
path.indexOf("?") > ||
lastC === /* "/" */) ? 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()
// Absolute 絕對路徑
if (ABSOLUTE_RE.test(id)) {
ret = id
}
// Relative
else if (first === /* "." */) {
ret = (refUri ? dirname(refUri) : data.cwd) + id
}
// Root
else if (first === /* "/" */) {
var m = data.cwd.match(ROOT_DIR_RE)
ret = m ? m[] + id.substring() : id
}
// Top-level
else {
ret = data.base + id
}
// Add default protocol when uri begins with "//"
if (ret.indexOf("//") === ) {
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 =, len = map.length; i < len; i++) {
var rule = map[i]
ret = isFunction(rule) ?
(rule(uri) || uri) :
uri.replace(rule], rule])
// Only apply the first matched rule
if (ret !== uri) break
}
}
return ret
}
總結
以上貼上了seajs的相關源碼,最其中的思想是很佩服的。通過了解這些實作過程,對于我們使用seajs将會更加的便利。