這是對vue-router 3 版本的源碼分析。
本次分析會按以下方法進行:
- 按官網的使用文檔順序,圍繞著某一功能點進行分析。這樣不僅能學習優秀的項目源碼,更能加深對項目的某個功能是如何實現的理解。這個對自己的技能提升,甚至面試時的回答都非常有幫助。
- 在圍繞某個功能展開講解時,所有不相干的內容都會暫時去掉,等后續涉及到對應的功能時再加上。這樣最大的好處就是能循序漸進地學習,同時也不會被不相干的內容影響。省略的內容都會在代碼中以…表示。
- 每段代碼的開頭都會說明它所在的文件目錄,方便定位和查閱。如果一個函數內容有多個函數引用,這些都會放在同一個代碼塊中進行分析,不同路徑的內容會在其頭部加上所在的文件目錄。
本章講解router中別名是如何實現的。
另外我的vuex3源碼分析也發布完了,歡迎大家學習:
vuex3 最全面最透徹的源碼分析
還有vue-router的源碼分析:
vue-router 源碼分析——1. 路由匹配
vue-router 源碼分析——2. router-link 組件是如何實現導航的
vue-router 源碼分析——3. 動態路由匹配
vue-router 源碼分析——4.嵌套路由
vue-router 源碼分析——5.編程式導航
vue-router 源碼分析——6.命名路由
vue-router 源碼分析——7.命名視圖
vue-router 源碼分析——8.重定向
官網示例:
- /a 的別名是 /b,意味著,當用戶訪問 /b 時,URL 會保持為 /b,但是路由匹配則為 /a,就像用戶訪問 /a 一樣。
- “別名”的功能讓你可以自由地將 UI 結構映射到任意的 URL,而不是受限于配置的嵌套路由結構。
const router = new VueRouter({routes: [{ path: '/a', component: A, alias: '/b' }]
})
Router初始化時對別名的處理
- 路由的別名和重定向一樣,也會記錄到路由記錄上。通過下面的源碼分析可以發現,別名的數據結構是[]string,這表明我們在定義一個路由的別名時,除了像上面的官網例子中使用字符串path外,還能定義一個數組。這是官網中沒有說明的功能。
- 之后,遍歷別名數組,創建了一個基于別名路徑的路由記錄。別名路由記錄可以簡單看成除了path,matchAs屬性不同外,其他的內容都和’/a’路由記錄相同。
// ./create-route-map.js
function addRouteRecord(...
) {const { path, name } = route...const record: Record = {alias: route.alias? typeof route.alias === 'string'? [route.alias]: route.alias: [],... }...if (route.alias !== undefined) {const aliases = Array.isArray(route.alias) ? route.alias : [route.alias]for(let i = 0; i < aliases.length; ++i) {const alias = aliases[i]...const aliasRoute = {path: alias,children: route.children }addRouteRecord(pathList,pathMap,nameMap,aliasRoute,parent,record.path || '/' // matchAs )}}
}
訪問別名時的邏輯
- 當用戶訪問的是別名’/b’的url時,觸發的路由匹配會匹配到上面描述的別名路由記錄。然后在生成路由時,調用alias函數執行別名路由生成操作。
- 在alias函數中,通過別名路由的matchAs屬性(即正式路徑’/a’),進行真正的路由匹配,再創建對應路由。
- 這樣,就實現了router的別名功能。
// ./create-matcher.js
export function createMatcher(routes: Array<RouteConfig>,router: VueRouter
): Matcher {...function _createRoute(record: ?RouteRecord,location: Location,redirectedFrom?: Location): Route {...if (record && record.matchAs) {return alias(record, location, record.matchAs) }... }function alias(record: RouteRecord,location: Location,matchAs: string): Route {const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`)const aliasedMatch = match({_normalized: true,path: aliasedPath })if (aliasedMatch) {const matched = aliasedMatch.matchedconst aliasedRecord = matched[matched.length - 1]location.params = aliasedMatch.paramsreturn _createRoute(aliasedRecord, location)}return _createRoute(null, location)}
}