首页 > 编程语言 >vue-router 源码分析——2. router-link 组件是如何实现导航的

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

时间:2024-06-04 22:00:16浏览次数:44  
标签:... vue const route 源码 location path router

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

在这里插入图片描述

标签:...,vue,const,route,源码,location,path,router
From: https://blog.csdn.net/miyagiSimple/article/details/139454310

相关文章

  • 基于微信小程序的健身小助手打卡预约教学系统(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于Java的电影评论管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的足球社区管理系统(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的医院住院管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • Vue3单文件组件实现省市区县三级联动
    Provinces.vue<template>  <divclass="select-container">    <selectv-model="selectedProvince"@change="handleProvinceChange">      <optionvalue=""disabled>请选择省份</opti......
  • 使用yolov10源码对图片进行目标检测的步骤讲解(windows环境、PyCharm软件、预测阶段)
            本文只讲解yolov10的预测阶段,即如何使用代码调用yolov10模型,对图片进行目标检测,并不涉及训练、验证等阶段。步骤:一、下载yolov10源码        1、打开源码地址。yolov10源码链接:https://github.com/THU-MIG/yolov10    注意:如果网页加载不......
  • Redisson 限流器源码分析
    Redisson限流器源码分析对上篇文章网友评论给出问题进行解答:redis的key是否会过期可以先阅读上篇文章:redis+AOP+自定义注解实现接口限流-古渡蓝按-博客园(cnblogs.com)注解AOP代码部分提取//调用Reids工具类的rateLimiter方法longnumber=RedisUtils.rat......
  • 基于springboot-vue的毕业论文管理系统(11728)
     有需要的同学,源代码和配套文档领取,加文章最下方的名片哦一、项目演示二、资料项目演示视频介绍完整源代码(前后端源代码+SQL脚本)配套文档(LW+PPT+开题报告)远程调试控屏包运行三、技术介绍Java语言SSM框架SpringBoot框架Vue框架JSP页面Mysql数据库IDEA/Eclipse开发四、项......
  • 新版校园跑腿外卖独立版+APP+小程序前端外卖配送平台源码
    源码介绍:同城校园跑腿外卖配送平台源码,支持自定义diy你可以设计你的页面,设计你自己的风格,支持多校园,独立版本,多商户,有用户端,骑手端,商家端,强大的功演示截图:安装说明:服务器2h4G5M即可,服务器系统:centos8,服务器上安装宝塔面板,不懂怎么安装宝塔的可以百度,所需环境:php7.3......
  • containerd 源码分析:创建 container(三)
    文接containerd源码分析:创建container(二)1.2.2.2启动task上节介绍了创建task,task创建之后将返回response给ctr。接着,ctr调用task.Start启动容器。//containerd/client/task.gofunc(t*task)Start(ctxcontext.Context)error{ r,err:=t.client.TaskServi......