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

2024-06-05 07:28

本文主要是介绍vue-router 源码分析——2. router-link 组件是如何实现导航的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

  1. 按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时的回答都非常有帮助。
  2. 在围绕某个功能展开讲解时,所有不相干的内容都会暂时去掉,等后续涉及到对应的功能时再加上。这样最大的好处就是能循序渐进地学习,同时也不会被不相干的内容影响。省略的内容都会在代码中以…表示。
  3. 每段代码的开头都会说明它所在的文件目录,方便定位和查阅。如果一个函数内容有多个函数引用,这些都会放在同一个代码块中进行分析,不同路径的内容会在其头部加上所在的文件目录。

本章讲解router中 router-link 组件是如何实现导航的。
另外我的vuex3源码分析也发布完了,欢迎大家学习:
vuex3 最全面最透彻的源码分析
还有vue-router的源码分析:
vue-router 源码分析——1. 路由匹配
vue-router 源码分析——2. router-link 组件是如何实现导航的

官网例子

  • 在官网例子中,对做了三个注释:这是个组件、传入to属性,渲染成标签。
  • 以我们主要分析 router-link 组件如果利用必要的数据来实现导航的。
  • 这里先解释一下,渲染html页面的功能,是vue-router调用了vue的render函数,这个是vue的核心功能不做分析。所以这里是分析router是如何定位到对应路由以及做了哪些信息收集和处理的。
<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源码内容

  • 传入了一个to变量,对应’/foo’,tag默认为’a’,即标签.
  • render函数是vue用来描述如果构建虚拟DOM的,即如何按某个定义来构建组件。
  • 如何渲染为html是vue中的核心内容,这里不做讨论,只分析vue-router中的源码内容,即resolve方法。
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.fullPathconst base = this.history.baseconst 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 } : rawif (next._normalized) {return next    }...// parsePath 的实际内容为 {'path': '/foo', 'query': '', 'hash': ''}const parsedPath = parsePath(next.path || '')// basePath 为当前的路由的路径const basePath = (current && current.path) || '/'// resolvePath函数对'/foo'也没有额外影响,可以理解直接返回了'/foo'赋值给pathconst 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 } = locationif (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 : fullPathreturn base ? cleanPath(base + '/' + path) : path
}

总结

  • recolve返回的对象里面的内容主要为location, route , href。
  • location是标准化后的to,并且打上了标记表示已标准化,防止多次标准化,提升效率。
  • route是通过遍历pathList和pathMap,利用正则表达式找到的和to匹配的路由对象,里面包含很多需要的内容。
  • href在默认的hash模式下,会在to的前面加上#号,例如这里的’#/foo’。
// ./router.jsexport 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.fullPathconst base = this.history.baseconst href = createHref(base, fullPath, this.mode)return {location,route,href,...        }    }
}
  • 后续的内容就是vue利用h函数将link组件渲染为html的内容,例如设置类名,定义handler函数处理跳转,绑定点击事件,指定 a 标签等等。
  • 这里再对vue-router官方文档中提到的匹配成功后自动设置的class属性值,通过源码分析一下:
    • 通过下面的代码可以看出,如果你不想要 router-link-active 的类名,可以在初始化router时,加入options.linkActiveClass属性就可以了

在这里插入图片描述

这篇关于vue-router 源码分析——2. router-link 组件是如何实现导航的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1032440

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi