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.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 标签等等。
  • 这里再对vue-router官方文档中提到的匹配成功后自动设置的class属性值,通过源码分析一下:
    • 通过下面的代码可以看出,如果你不想要 router-link-active 的类名,可以在初始化router时,加入options.linkActiveClass属性就可以了

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用raise语句抛出异常

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 如果某个函数或方法可能会产生异常&#xff0c;但不想在当前函数或方法中处理这个异常&#xff0c;则可以使用raise语句在函数或方法中抛出异常。rai…

车联网安全之GPS欺骗

随着车辆GPS技术的广泛应用&#xff0c;其在导航、交通监控等方面的关键性日益凸显&#xff0c;与之相关的安全性问题也备受关注。GPS欺骗便是其中一种重要的威胁手段。 本文旨在使用模拟GPS欺骗设备&#xff0c;模拟不同的GPS攻击以评估其对车辆定位系统的影响。通过这个实验…

「不只是框架:Django REST framework的超能力大揭秘」

想要让你的API服务像五星级餐厅一样令人难忘吗&#xff1f;今天阿佑将为你揭晓&#xff01;从基础的RESTful原则到Django REST framework的高级特性&#xff0c;我们一步步带你走进API开发的后厨&#xff0c;展示如何准备食材&#xff08;数据模型&#xff09;、调制酱料&#…

记录一次云服务器无法连接的排查过程

运行环境&#xff1a;阿里云服务器 故障外显原因&#xff1a;登录失败,操作系统禁用了密码登录方式 控制台监控数据显示云盘读写BPS拉满了 因为之前问过线上售后&#xff0c;让安装了atop监控&#xff0c;&#xff0c;所以先打开atop日志&#xff1a; atop -r /var/log/atop…

try…except…else语句

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中&#xff0c;还有另一种异常处理结构&#xff0c;它是try…except…else语句&#xff0c;也就是在原来try…except语句的基础上再添加一个…

关于博图17安装体验过程—博图17安装失败原因(STEP7 许可证找不到)

目录 一、序言 二、正片 一、序言 该失败原因是在我使用Win11专业版安装博图17时出现的问题&#xff0c;也仅代表我的体验过程&#xff01;以下我将安装过程和解决问题的过程描述一下&#xff0c;希望可以帮助和我一样自己安装博图时能够解决出现的问题。 二、正片 如果阁下…

DRIVEN|15分的CNN+LightGBM怎么做特征分类,适用于转录组

说在前面 今天分享一篇做深度学习模型的文章&#xff0c;这是一篇软硬结合的研究&#xff0c;排除转换实体产品&#xff0c;我们做生信基础研究的可以学习模仿这个算法&#xff0c;适用且不局限于临床资料&#xff0c;转录组数据&#xff0c;GWAS数据。 今天给大家分享的一篇文…

构造+割点,F2. Spanning Tree with One Fixed Degree

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1133F2 - Codeforces 二、解题报告 1、思路分析 考虑以根节点为割点&#xff0c;会有若干个连通块 连通块的数目为根节点至少要连出去的边&#xff0c;不妨记为mi 如果mi > D&#xff0c;那…

分享一个 ASP.NET WebForm 使用 Form Authentication 的例子

前言 前些天一个朋友说他们客户的网站出了点故障&#xff0c;让我帮忙看看&#xff0c;这个网站还是用 ASP.NET WebForm 做的&#xff0c;很久以前的技术了&#xff0c;不过很多客户就是这样&#xff0c;只要网站还能稳定地运行&#xff0c;一般就不会去折腾升级&#xff0c;_…

未来以来!鸿蒙生态爆发式增长,程序员新出路火速Get。

鸿蒙生态取得爆发式增长&#xff01; 鸿蒙生态建设速度突飞猛进&#xff0c;不仅有超4000款应用加速开发&#xff0c;众多头部SDK伙伴也在积极加入&#xff0c;为开发者提供构建鸿蒙原生应用所需的多项能力。近期&#xff0c;友盟移动统计SDK、神策数据SDK、阿里云日志服务SDK…

【TB作品】msp430g2553单片机,秒表,LCD1602,Proteus仿真

功能 秒表 动图&#xff1a; 部分代码 这段代码是用C语言编写的&#xff0c;用于在基于德州仪器MSP430微控制器的平台上实现一个简易的电子秒表功能。 #include <msp430.h> #include "LCD.h"unsigned int second 0; unsigned int millisecond10…

向量化:机器学习中的效率加速器与数据桥梁

在机器学习领域的广袤天地中&#xff0c;向量化技术以其独特的魅力&#xff0c;为数据处理和模型训练注入了强大的动力。本文将深入探讨向量化在机器学习领域中的体现&#xff0c;剖析其如何助力模型实现高效的数据处理和精确的结果预测&#xff0c;并通过丰富的案例和详尽的数…

一文了解JVM(中)

HotSpot 虚拟机对象探秘 对象的创建 Header解释使用 new 关键字调用了构造函数使用 Class 的 newInstance 方法调用了构造函数使用 Constructor 类的newInstance 方法调用了构造函数使用 clone 方法没有调用构造函数使用反序列化没有调用构造函数说到对象的创建,首先让我们看…

路由策略简介

一、路由策略 1、定义: 路由策略(RoutingPolicy)作用于路由&#xff0c;主要实现了路由过滤和路由属性设置等功能&#xff0c;它通过改变路由属性(包括可达性)来改变网络流量所经过的路经。 2、目的 设备在发布、接收和引入路由信息时&#xff0c;根据实际组网需要实施一些策…

【深度学习代码缝合教程】二:适用于新手小白的超详细模块+模块=新模块的代码缝合

参考B站教学视频&#xff1a; 深度学习网络缝合模块&#xff0c;模块缝模块 如何对主干网络模块进行代码缝合&#xff1a; 【深度学习代码缝合教程】一&#xff1a;适用于新手小白的超详细深度学习主干网络模块代码缝合 上一篇写了如何把模块放进自己的主干网络进行模块的融合…

SEO代理是什么?代理IP在SEO优化中的应用

在搜索引擎优化 (SEO) 领域&#xff0c;拥有一个好的代理对于取得成功至关重要。代理充当您的设备和互联网之间的中介&#xff0c;允许您隐藏您的 IP 地址并使用不同的 IP 访问网络。在这篇博文中&#xff0c;我们将探讨为什么好的代理对 SEO 至关重要&#xff0c;以及它如何有…

【UnityShader入门精要学习笔记】第十七章 表面着色器

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 表面着色器…

重学java 63.IO流 字节流 ④ 文件复制

身处泥泞&#xff0c;看满山花开 —— 24.6.4 图片复制 分析 1.创建两个对象 FilelnputStream —>读取指定的文件 FileOutputStream —> 将读到的字节写到指定的位置 2.边读边写 import java.io.FileInputStream; import java.io.FileOutputStream;public class Demo…

vue+vscode 快速搭建运行调试环境与发布

1.安装node.js Node.js — Run JavaScript Everywhere 默认不断next 2.更换镜像地址 运行-cmd 执行以下代码安装 npm config set registry https://registry.npmmirror.com 检查node.js和镜像是否是否成功 node -v npm -v npm config get registry 3.安装打包工具 …