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属性就可以了

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340859.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

德人合科技——@天锐绿盾 | -文档透明加密系统

天锐绿盾文档透明加密系统是一种先进的数据安全解决方案&#xff0c;旨在保护企业和组织的敏感信息&#xff0c;防止未经授权的访问和泄漏。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是该系统的一些关键特点和功…

【读书笔记】曼陀罗思考法

目录 1 起源2 路径示例——人生规划设计 3 分类3.1 扩展型“扩展型”曼陀罗——使用方法 3.2 围绕型 4 注意事项 1 起源 曼陀罗在梵文中意味着“圣地”&#xff0c;象征着宇宙的秩序和内心的神圣结构。 “曼陀罗思考法”&#xff0c;是由日本学者今泉浩晃发明的方法&#xff…

【计算机毕设】基于SpringBoot的中小企业设备管理系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 在中小企业中&#xff0c;设备管理是确保生产和运营效率的重要环节。传统的设备管理通常依赖于手工记录和人工管理&#xff0c;容易导致数据不准确、…

LLM的基础模型4:初识Embeddings

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

12-学生们参加各科测试的次数(高频 SQL 50 题基础版)

12-学生们参加各科测试的次数 -- 学生表中&#xff0c;id是唯一的&#xff0c;将他作为主表 -- CROSS JOIN产生了一个结果集&#xff0c;该结果集是两个关联表的行的乘积 -- 2行表,与3行表使用cross join,得到2*36行数据 select st.student_id, st.student_name,su.subject_na…

【软件测试】自动化测试如何管理测试数据

前言 在之前的自动化测试框架相关文章中&#xff0c;无论是接口自动化还是UI自动化&#xff0c;都谈及data模块和config模块&#xff0c;也就是测试数据和配置文件。 随着自动化用例的不断增加&#xff0c;需要维护的测试数据也会越来越多&#xff0c;维护成本越来越高&#…

【Transformer(7)】Transformer架构解析

一、Transformer结构图 从上图可以看到&#xff1a; Transformer结构主要由编码和解码两大部分组成&#xff1a; &#xff08;1&#xff09;输入- position embedding - patch embedding &#xff08;2&#xff09;编码器 多头注意力机制 Add & NormMLP Add & Norm &…

爪哇,我初窥门径

2017年3月&#xff0c;我大二下学期了。 虽说一直在学习&#xff0c;持续在解决学习中遇到的问题&#xff0c;但迷茫依旧。 对着黑框编程&#xff0c;还是不知道Java在现实工作中是用来干什么的。 说实话&#xff0c;真的挺枯燥无趣的。 逐渐&#xff0c;我开始意识到&#…

linux部署运维2——centos7.9离线安装部署涛思taos2.6时序数据库TDengine

在实际项目开发过程中&#xff0c;并非一直都使用关系型数据库&#xff0c;对于工业互联网类型的项目来说&#xff0c;时序型数据库也是很重要的一种&#xff0c;因此掌握时序数据库的安装配置也是必要的技能&#xff0c;不过对于有关系型数据库使用的开发工作者来说&#xff0…

基础数学内容重构(后缀0个数)

今天也是参加了一下宁波大学的校赛&#xff0c;其中有一道题是求后缀0的个数&#xff0c;题意是让我们求一下式子的后缀0个数&#xff1a; 看上去比较复杂&#xff0c;但是通过化简我们可以知道以上式子就是求&#xff08;n 1&#xff09;&#xff01;&#xff0c;这里化简的过…

windows上进行git初始化时报错:fatal: unknown write failure on standard output

一、报错描述 1、git init命令一般是在命令行&#xff0c;切换到项目的根目录后执行 2、如果是windows的系统&#xff0c;我们粘贴路径时&#xff0c;需要进行转义命令行才能识别&#xff0c; 也就是像我下面写的 D:\\Users\\...3、报错信息进行解读 一般情况下&#xff0c;…

2024年手机能做的赚钱软件有哪些?整理了八个手机能做的正规赚钱软件分享

在这个指尖滑动的时代&#xff0c;手机不仅仅是通讯工具&#xff0c;更是我们探索财富的钥匙。你是否曾幻想过&#xff0c;躺在沙发上&#xff0c;轻轻一滑&#xff0c;就能让钱包鼓起来&#xff1f; 今天&#xff0c;就让我们一起来探索那些隐藏在手机里的赚钱秘笈&#xff0c…

Apache OFBiz 路径遍历导致RCE漏洞复现(CVE-2024-36104)

0x01 产品简介 Apache OFBiz是一个电子商务平台,用于构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类应用系统。是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。 0x02 漏洞概…

缓存方法返回值

1. 业务需求 前端用户查询数据时,数据查询缓慢耗费时间; 基于缓存中间件实现缓存方法返回值:实现流程用户第一次查询时在数据库查询,并将查询的返回值存储在缓存中间件中,在缓存有效期内前端用户再次查询时,从缓存中间件缓存获取 2. 基于Redis实现 参考1 2.1 简单实现 引入…

STM32 HAL库开发——入门篇(3):OLED、LCD

源自正点原子视频教程&#xff1a; 【正点原子】手把手教你学STM32 HAL库开发全集【真人出镜】STM32入门教学视频教程 单片机 嵌入式_哔哩哔哩_bilibili 一、OLED 二、内存保护&#xff08;MPU&#xff09;实验 2.1 内存保护单元 三、LCD 3.1 显示屏分类 3.2 LCD简介 3.3 LCD…

jpeg编码学习

正点原子stm32教程提到过jpeg解码库libjpeg&#xff0c;但是没有提到jpeg编码&#xff0c;我也好奇jpeg编码怎么实现&#xff0c;用代码怎么生成jpeg文件的。所以最近学习了jpeg编码&#xff0c;在这里做记录。 参考文章 jpeg图片格式详解 https://blog.csdn.net/yun_hen/art…

算法004:盛水最多的容器

这道题比较简单&#xff0c;使用双指针。 要求的是最大面积&#xff0c;对于一个水桶&#xff08;水杯来说&#xff09;&#xff0c;面积的算法是固定的&#xff0c;就是底乘以高。 在这个题中&#xff0c;我们把左边的位置设为left&#xff0c;右边的位置设为right&#xff…

Python语法详解module1(变量、数据类型)

目录 一、变量1. 变量的概念2. 创建变量3. 变量的修改4. 变量的命名 二、数据类型1. Python中的数据类型2. 整型&#xff08;int&#xff09;3. 浮点型&#xff08;float&#xff09;4. 布尔型&#xff08;bool&#xff09;5. 字符串&#xff08;str&#xff09;6.复数&#xf…

企业微信接入系列-上传临时素材

企业微信接入系列-上传临时素材 文档介绍上传临时素材写在最后 文档介绍 创建企业群发的文档地址&#xff1a;https://developer.work.weixin.qq.com/document/path/92135&#xff0c;在创建企业群发消息或者群发群消息接口中涉及到上传临时素材的操作&#xff0c;具体文档地址…

OJ题目【栈和队列】

题目导入 栈&#xff1a; 题目一&#xff1a;有效的括号题目二&#xff1a;用栈实现队列 队列 题目&#xff1a;实现循环队列 栈 题目一 有效的括号 题目要求 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘…