这是对vue-router 3 版本的源码分析。
本次分析会按以下方法进行:
- 按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时的回答都非常有帮助。
- 在围绕某个功能展开讲解时,所有不相干的内容都会暂时去掉,等后续涉及到对应的功能时再加上。这样最大的好处就是能循序渐进地学习,同时也不会被不相干的内容影响。省略的内容都会在代码中以…表示。
- 每段代码的开头都会说明它所在的文件目录,方便定位和查阅。如果一个函数内容有多个函数引用,这些都会放在同一个代码块中进行分析,不同路径的内容会在其头部加上所在的文件目录。
本章讲解router中嵌套路由是如何实现的。
另外我的vuex3源码分析也发布完了,欢迎大家学习:
vuex3 最全面最透彻的源码分析
还有vue-router的源码分析:
vue-router 源码分析——1. 路由匹配
vue-router 源码分析——2. router-link 组件是如何实现导航的
vue-router 源码分析——3. 动态路由匹配
vue-router 源码分析——4.嵌套路由
官方例子:
// 创建的 app
<div id="app">
<router-view></router-view>
</div>
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
RouteRecord 记录
- 在构建 RouteRecord 时,子路由会记录其父路由到 parent 属性上。
// create-route-map.js
function addRouteRecord(
pathList: Array<string>,
pathMap: Dictionary<RouteRecord>,
nameMap: Dictionary<RouteRecord>,
route: RouteConfig,
parent?: RouteRecord,
matchAs?: string
) {
const { path, name } = route
const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict)
const record: RouteRecord = {
path: normalizedPath,
components: route.components || { default: route.component},
parent,
...
}
if (route.children) {
...
route.children.forEach(child => {
// 递归调用 addRouteRecord,将父record加入到子record的parent中
addRouteRecord(pathList, pathMap, nameMap, child, record, undefined)
})
}
}
- 在匹配完路由后,创建路由时,会按 [父record,子reocrd…] 的顺序结构创建一个matched对象在创建的路由对象 Route 上。
// ./util/route.js
export function createRoute(
record: ?RouteRecord,
location: Location,
redirectedFrom?: ?Location,
router?: VueRouter
): Route {
const route: Route = {
path: location.path || '/',
params: location.params || {},
matched: record ? formatMatch(record) : [],
...
}
...
return Object.freeze(route)
}
function formatMatch(record: ?RouteRecord): Array<RouteRecord> {
const res = []
while (record) {
res.unshift(record)
record = record.parent
}
return res
}
- 在渲染路由时,从根节点的开始依次渲染,通过depth来记录当前的嵌套深度,按照当前深度取出matched中对应的渲染数据。(Vue对router-view的渲染,是从最顶层的开始渲染的)
// ./components/view.js
export default {
...
render(_, { props, children, parent, data }) {
data.routerView = true
const h = parent.$createElement
const route = parent.$route
let depth = 0
while (parent && parent._routerRoot !== parent) {
const vnodeData = parent.$vnode ? parent.$vnode.data : {}
// 如果父节点也是router-view,则深度+1
if (vnodeData.routerView) {
depth++
}
parent = parent.$parent
}
data.routerViewDepth = depth
// 取出对应深度的 matched和component 作为当前url对应路由的渲染数据
const matched = route.matched[depth]
const component = matched && matched.components[name]
if (!matched || !component) {
return h()
}
...
return h(component, data, children)
}
}
- 以上就是嵌套路由的实现源码
1320

被折叠的 条评论
为什么被折叠?



