vue-router 源码分析——4.嵌套路由

这是对vue-router 3 版本的源码分析。
本次分析会按以下方法进行:

  1. 按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时的回答都非常有帮助。
  2. 在围绕某个功能展开讲解时,所有不相干的内容都会暂时去掉,等后续涉及到对应的功能时再加上。这样最大的好处就是能循序渐进地学习,同时也不会被不相干的内容影响。省略的内容都会在代码中以…表示。
  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: `
    

User {{ $route.params.id }}

`
} const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ { // 当 /user/:id/profile 匹配成功, // UserProfile 会被渲染在 User 的 path: 'profile', component: UserProfile }, { // 当 /user/:id/posts 匹配成功 // UserPosts 会被渲染在 User 的 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)
    }
}
  • 以上就是嵌套路由的实现源码

你可能感兴趣的:(前端,vue.js,前端,javascript)