vue-router 源码分析——2. router-link 组件是如何实现导航的

小伙伴们晚上好,今天继续发vue-router的源码分析。

另外今天我所在的小区超市被零元购了哈哈,超市老板欠债,债权人来超市搬货,最终JC介入。

可见现在的经济有多差。

官网例子

<script src="https://unpkg.com/vue@2/dist/vue.js"></script> <script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script> <div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> </div>

Vue实例挂载link组件,从而可以使用标签

  • 这里的install.js实际上是一个VUE的插件,这样当创建和挂载根实例时自动导入了插件。

  • 这里面还涉及到VUE的混入(Vue.mixin),实现了在任意组件内通过this.$ router和this.$route来访问路由器和当前路由。感兴趣的小伙伴可以去看VUE官网的解释。

// ./install.js import Link from './components/link' export function install(Vue) { Vue.component('RouterLink', Link) }

link.js源码内容

const { location, route, href } = router.resolve( this.to, current, this.append ) ... const classes = {} const data: any = { class: classes } ... return h(this.tag, data, this.$slots.default) } }

路由实例的 resolve 方法

  • resolve入参的to是我们传入的字符串'/foo',current是当前路由this.$route,append是undefine。

  • resolve 方法内部又调用了很多函数和方法,得到需要的数据并返回。

  • 让我们接着分析调用的函数(normalizeLocation,this.match, createHref)。

// ./router.js export default class VueRouter { ... resolve ( to: RawLocation, current?: Route, append?: boolean ) { ... const location = normalizeLocation(to, current, append, this) const route = this.match(location, current) const fullPath = route.redirectedFrom || route.fullPath const base = this.history.base const href = createHref(base, fullPath, this.mode) // this.mode 看做 'hash' 即可 return { location, route, href ... } } }

normalizeLocation 函数分析

  • 入参说明:raw = ‘/foo', current = this.route, append = undifune, router = this.$router

  • parsePath函数暂时没有额外影响,这里不做分析,只是说明 parsePath 的具体内容

  • 最终返回一个对象,里面有 _normalized: true 和 path: '/foo'相关内容。

// ./util/location.js export function normalizeLocation( raw: RawLocation, current: ?Route, append: ?boolean, router: ?VueRouter ): Location { let next: Location = typeof raw === 'string' ? { path: raw } : raw if (next._normalized) { return next } ... // parsePath 的实际内容为 {'path': '/foo', 'query': '', 'hash': ''} const parsedPath = parsePath(next.path || '') // basePath 为当前的路由的路径 const basePath = (current && current.path) || '/' // resolvePath函数对'/foo'也没有额外影响,可以理解直接返回了'/foo'赋值给path const path = parsedPath.path ? resolvePath(parsedPath.path, basePath, append || next.append) : basePath ... return { _normalized: true, path, ... } }

this.match方法获得的route是什么

  • 对应代码 const route = this.match(location, current),current = this.$route。

  • match方法和resolve方法一样,是定义在VueRouter中的,它直接返回了路由匹配器的match方法。

  • 路由匹配器中的match方法会遍历pathList和pathMap,利用正则表达式查看对应的path是否匹配,如果匹配,这里则返回 _createRoute 函数调用。

    • pathList和pathMap是在初始化router时生成的,这里为方便理解,再说明一下。

    • pathList是一个数组,记录着我们初始化时定义的各个url path,例如'/foo','/bar'

    • pathMap是一个哈希结构,key为path,value为相关的数据(比如path, component, regex等等)。

  • _createRoute 函数又调用了 ./util/route.js 的 createRoute 函数。它返回了一个被冻结的对象,里面主要有path和matched属性,matched在这里可以看做等于 [record]。

  • 所以this,match方法返回的是一个和我们输入的路径to匹配的一个route对象,里面有我们需要的path,record等内容。

// ./router.js export default class VueRouter { ... match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route { return this.matcher.match(raw, current, redirectedFrom) } } // ./create-matcher.js export function createMatcher(...) { ... function match { raw: RawLocation, currentRoute?: Route, redirectedFrom?: Location }: Route { // 由于传入的raw是已经标准化过的,所以这里的location和raw没有任何区别 const location = normalizedLocation(raw, currentRoute, false, router) const { name } = location if (name) { ... } else if (location.path) { location.params = {} for (let i = 0; i < pathList.length; i++) { const path = pathList[i] const record = pathMap[path] if (matchRoute(record.regex, location.path, location.param)) { return _createRoute(record, location, redirectedFrom) } } } } function matchRoute( regex: RouteRegExp, path: string, params: Object ): boolean { const m = path.match(regex) if (!m) { return false } else if (!params) { return true } ... return true } function _createRoute( record: ?RouteRecord, location: Location, redirectedFrom?: Location ): Route { ... return createRoute(record, location. redirectedFrom, router) } } // ./util/route.js export function createRoute( record: ?RouteRecord, location: Location, redirectedFrom?: ?Location, router?: VueRouter ): Route { const route: Route = { path: location.path || '/', matched: record ? formatMatch(record) :[], // 这里可以先简单理解为 [record] ... } return Object.freeze(route) }

createHref函数分析

  • 对应代码 href = createHref(base, fullPath, this.mode),对应base = undefine, fullPath = '/foo', this.mode = 'hash'。

  • 在vue-route中,默认为hash模式,所以所有的的path前面都会带一个#号

  • 这个#号就是在这个函数中体现的

// ./router.js function createHref(base: string, fullPath: string, mode) { var path = mode === 'hash' ? '#' + fullPath : fullPath return base ? cleanPath(base + '/' + path) : path }

总结

  • recolve返回的对象里面的内容主要为location, route , href。

  • location是标准化后的to,并且打上了标记表示已标准化,防止多次标准化,提升效率。

  • route是通过遍历pathList和pathMap,利用正则表达式找到的和to匹配的路由对象,里面包含很多需要的内容。

  • href在默认的hash模式下,会在to的前面加上#号,例如这里的'#/foo'。

// ./router.js export default class VueRouter { ... resolve(to, current, append) { const location = normalizeLocation(to, current, append, this) const route = this.match(location, current) const fullPath = route.redirectedFrom || route.fullPath const base = this.history.base const href = createHref(base, fullPath, this.mode) return { location, route, href, ... } } }

  • 后续的内容就是vue利用h函数将link组件渲染为html的内容,例如设置类名,定义handler函数处理跳转,绑定点击事件,指定 a 标签等等。
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
zest
作者分享
最近业务需要,计划招多名短期兼职,时长半个月左右,做数据标注任务。 要求理解标注规则,(修改一下)按数量付钱,多劳多得,但是必须在某个规定时间内完成,需要有充足的时间。最好有标注经验。 可远程,有需要的结束后可开实习证明。 感兴趣的加我微信详谈。
7
有没有学习 Python 的鱼友,有用 Python 开发鱼皮项目的经验。 boss 上看了一堆都没匹配上,在这里随缘发一发。 应届或者毕业一年以内,计算机基础扎实,刷过题,有项目经验。 base 深圳南山区,薪资 15k-20k。 除了后端,还能接触到前端和大模型的方向,对职业发展非常有帮助。 感兴趣并且符合要求的加我微信 15773283322
5
新年伊始,招聘一名全栈(偏前端)工程师
5
站在面试官的角度,分析如何更好地应对面试
15
我又来招人了~ 这次是招一名实习生,做 AI 大模型 agent 应用开发。 基于上次招人的情况,我这里做了一些规定和要求,希望有意向的小伙伴能认真看完下面的内容并考虑清楚后再来投递简历。 岗位名称:大模型应用开发工程师 地点:深圳南山科技园 时间:至少实习3个月,周末双休 年限要求:应届实习 岗位要求: 1. 计算机相关专业,最好是大四或研二; 2. 熟悉 Python 语言,具有扎实的数据结构和算法功底; 3. 对基于AI大模型的应用开发感兴趣; 4. 时间比较充裕,能到公司坐班; 5. 良好的沟通能力、学习能力、和团队协作精神; 工作内容: 1. 基于大模型的 agent 应用开发; 2. 基于场景对 Prompt 进行设计和测评,提升大模型效果和稳定性; 3. 根据公司业务需求,与开发团队、产品运营团队紧密配置,持续改进应用; 感兴趣的小伙伴可以添加我微信 15773283322 --------------------------------------------------------------- 随便说一下,上次发布的招聘,也成功地在编程导航上找到了满意的候选人,目前已经入职半个月多了。 上次招人收到了很多人的简历,但是就简历质量而言,真的是一言难尽。 这周末我找时间输出一篇站在面试官的角度看待招聘过程的感受,写一些对焦虑难找工作的人的一些建议。 这里提一句就是真的要好好准备简历啊!
9
下载 APP