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

news2024/11/26 18:23:53

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

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

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

官网例子

  • 在官网例子中,对做了三个注释:这是个组件、传入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属性就可以了

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VL830 USB4 最高支持40Gbps芯片功能阐述以及原理图分享

前文斥巨资拆了一个扩展坞供大家参考。其中核心即为本文要说的这个VL830,USB4的HUB芯片。 拆解报告传送门&#xff1a;USB4 Gen3x2 最高40Gbps传输速率的HUB扩展坞拆解分析 OK&#xff0c;闲话少叙。直接进入主题&#xff0c;我就直接翻译规格书了。 VL830是一款USB4端点设备…

Java学习54-关键字this的使用

this是什么 this的作用&#xff1a; 它在方法(准确的说是实例方法或非static的方法)内部使用&#xff0c;表示调用该方法的对象 它在构造器内部使用&#xff0c;表示该构造器正在初始化的对象 this可以调用的结构&#xff1a;成员变量、方法和构造器 什么时候使用this 实…

深度学习:如何静悄悄地改变我们的日常生活

深度学习 深度学习&#xff1a;如何静悄悄地改变我们的日常生活一、消费电子产品智能手机与个人助理娱乐与社交媒体 二、医疗健康三、汽车与交通四、公共安全五、总结 深度学习&#xff1a;如何静悄悄地改变我们的日常生活 在近年来&#xff0c;深度学习技术因其强大的数据处理…

fmsh:1 memorytest测试内存工程使用说明

1、如何导出 从procise中导出内存测试工程&#xff1a;memtest 点击ok按钮导出memtest工程成功。 2、引导iar启动&#xff0c;进入工程 1&#xff09;确保已经关联了iar工具 2&#xff09;启动iar 工程目录如下&#xff1a; 3、修改内存测试大小 4、压力测试下&#xff…

OS复习笔记ch7-3

承接上文我们讲完了页式管理和段式管理&#xff0c;接下来让我们深入讲解一下快表和二级页表 快表 快表和计算机组成原理讲的Cache原理如出一辙。为了减少访存的次数&#xff0c;OS在访问页面的时候创建了快表&#xff08;Translation Lookaside Buffer &#xff0c;简称TLB&…

大数据湖一体化平台整体建设方案(PPT原件)

背 景&#xff1a;大数据湖的发展背景与建设理念 体 系&#xff1a;大数据湖体系规划与建设思路 生态圈&#xff1a;探索新兴业务入湖建设模式 共 享&#xff1a;大数据湖统一访问共享规划 运 营&#xff1a;大数据湖一体化运营管理建设 软件全套资料部分文档清单&…

centos7 安装 mysql5.7 LTS

centos7 安装 mysql5.7 LTS 参考&#xff1a; https://blog.csdn.net/EB_NUM/article/details/105425622 可以在运行安装程序之前导入密钥&#xff1a; sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022第一步、下载MySQL 安装包&#xff1a; sudo wget h…

Vue第三方库与插件实战手册

title: Vue第三方库与插件实战手册 date: 2024/6/8 updated: 2024/6/8 excerpt: 这篇文章介绍了如何在Vue框架中实现数据的高效验证与处理&#xff0c;以及如何集成ECharts、D3.js、Chart.js等图表库优化数据可视化效果。同时&#xff0c;探讨了Progressive Web App(PWA)的接入…

【传知代码】DETR[端到端目标检测](论文复现)

前言&#xff1a;想象一下&#xff0c;当自动驾驶汽车行驶在繁忙的街道上&#xff0c;DETR能够实时识别出道路上的行人、车辆、交通标志等目标&#xff0c;并准确预测出它们的位置和轨迹。这对于提高自动驾驶的安全性、减少交通事故具有重要意义。同样&#xff0c;在安防监控、…

【Python】解决Python报错:KeyError: ‘username‘

​​​​ 文章目录 引言1. 错误详解2. 常见的出错场景2.1 用户输入处理错误2.2 动态数据源 3. 解决方案3.1 使用 get() 方法3.2 检查键是否存在 4. 预防措施4.1 数据验证4.2 使用默认字典 (defaultdict) 结语 引言 在Python开发中&#xff0c;处理字典时遇到 KeyError 是一种…

pikachu靶场全流程

目录​​​​​​​ 暴力破解&#xff1a; 1.基于表单的暴力破解&#xff1a; 2.验证码绕过(on server)&#xff1a; 3.验证码绕过(on client)&#xff1a; token防爆破&#xff1a; XSS&#xff1a; 1.反射型xss(get)&#xff1a; 2.反射性xss(post)&#xff1a; 3.存…

html页面上点击图片放大

需求&#xff1a; 我这里是搭配wangeditor插件使用&#xff0c;然后用直接拿到wangeditor输入的内容用dangerouslySetInnerHTML直接渲染的html页面&#xff0c;页面的代码里面并没有<p><p/><img />这类标签 dangerouslySetInnerHTML渲染如下所示&#xff1a…

mqtt-emqx:简单安装emqx

安装依赖 yum install -y epel-release libatomic下载 cd /chz/install/emqx wget https://www.emqx.com/en/downloads/broker/5.7.0/emqx-5.7.0-el7-amd64.tar.gz解压 mkdir -p emqx && tar -zxvf emqx-5.7.0-el7-amd64.tar.gz -C emqx后台运行 cd /chz/install/e…

STM32F103C8开发板 STM32最小系统核心板 AD硬件原理图+PCB封装文件分享

STM32F103C8开发板原理图 原理图和PCB下载地址&#xff1a; STM32F103C8开发板 STM32最小系统核心板 AD硬件原理图PCB封装文件.zip: https://url83.ctfile.com/f/45573183-1269573020-8f85b2?p7526 (访问密码: 7526)

【Vue】mutations

文章目录 一、定义mutations二、组件中提交 mutations三、带参数的 mutations 一、定义mutations mutations是vuex中的对象&#xff0c;这个对象可以定义在当前store的配置项中 const store new Vuex.Store({state: {count: 0},// 定义mutations// mutations是一个对象&#x…

小阿轩yx-iptables 防火墙

小阿轩yx-iptables 防火墙 Linux 防火墙基础 体系主要工作在 网络层针对TCP/IP 数据包实施过滤和限制 属于典型的包过滤防火墙&#xff08;或者称为网络层防火墙&#xff09; 体系基于内核编码实现 好处 具有非常稳定的性能高效率 防火墙两个表示 netfilteriptables …

IPD推行成功的核心要素(七)如何利用DSTE拉通产品战略与策略?

如果说许多企业以往的管理变革重在重塑创新价值流、营销价值流、供应链价值流、人力资源价值链等&#xff0c;那么&#xff0c;DSTE管理变革重塑的就是企业的“领导价值流”。从而使企业&#xff0c;在未来白热化的竞争中处于领导地位&#xff0c;在无序中给企业一个方向&#…

用幻灯片讲解内存分配器Allocator

用幻灯片讲解内存分配器Allocators Allocators 分配器 提供内存分配策略的通用接口委托给 C 运行时&#xff1a;new / delete块内存池不同大小的块内存池 为什么用分配器? 将容器逻辑与内存分配策略解耦速度&#xff1a;内存分配速度慢确保有足够的内存可用确保所需的内…

Large-Scale LiDAR Consistent Mapping usingHierarchical LiDAR Bundle Adjustment

1. 代码地址 GitHub - hku-mars/HBA: [RAL 2023] A globally consistent LiDAR map optimization module 2. 摘要 重建精确一致的大规模激光雷达点云地图对于机器人应用至关重要。现有的基于位姿图优化的解决方案&#xff0c;尽管它在时间方面是有效的&#xff0c;但不能直接…

GiantPandaCV | 提升分类模型acc(一):BatchSizeLARS

本文来源公众号“GiantPandaCV”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;提升分类模型acc(一)&#xff1a;BatchSize&LARS 在使用大的bs训练情况下&#xff0c;会对精度有一定程度的损失&#xff0c;本文探讨了训练的b…