我的源码学习之路(11)--- 延展vue-router

132 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情

前言

不甘于平庸又不努力

2023继续!!!


vue-router

首先下载一下源码git clone https://github.com/vuejs/vue-router.git(v3.6.5 for vue2)

image.png

项目中如何使用vue-router?

新建一个项目可以看到这样的文件

image.png

image.png

步骤就是: 安装、实例化、初始化

  1. import引入vue-router
  2. 用Vue.use() 安装 // 这个use的源码在之前已经写过了,这里简单提一下。
  3. 使用new Router()实例传递参数routers数组对象
  4. 最后使用new Vue

前端路由的具体流程:

  • 访问url时,例如/login,匹配器会拿着/home去路由映射表里面查找对应的组件,并将组件返回渲染,同时将访问记录推入历史栈

Vue.use(Router)

Vue.use的源码中提到,接收一个plugin,这时,这个plugin就可以看做Router,是要执行这个Router里面的install方法的,这说明插件里面一定要暴露一个install方法。当在代码里运行Vue.use(Router),内部就要执行 install,并且这个方法的第一个参数我们可以拿到Vue对象,这样好处是:

作为插件的编写方不需要再额外去import Vue

image.png

Router的install方法

vue-router的入口文件是[vue-router/src/index.js],进入后依次找到[vue-router\src\router.js],这时可以看到引入了install文件

import { install } from './install'
...
VueRouter.install = install // 当Vue.use(VueRouter)的时候 use里面就调用install方法 实际上就调用了router源码里的install方法
...
// 浏览器环境下自动安装插件
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter)
}

下一步,看一下install.js的代码主要干了啥(step by step)

// 引入router-view router-link组件
import View from './components/view' 
import Link from './components/link'

export let _Vue //export 一个Vue 引用

// 定义 安装install函数
export function install (Vue) {
  if (install.installed && _Vue === Vue) return  // 只会安装一次
  install.installed = true

  _Vue = Vue // 赋值给私有的Vue引用

  const isDef = v => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }
// 在beforeCreate,destroyed生命周期函数中注入路由逻辑。相当于给每个组件的beforeCreate,destroyed混入这段代码,每个组件都会运行
  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) {  判断this.$options.router在不在。(因为只有我们new Vue(VueRouter)时候,router才会被保存在vue根实例的$option上)
        this._routerRoot = this // this就是指的vue实例
        this._router = this.$options.router
        this._router.init(this) 初始化router
        Vue.util.defineReactive(this, '_route', this._router.history.current) // Vue中的响应式代码,把this._route变成响应式对象,保证_route变化时,router-view会重新渲染
      } else {
      // 为每个组件定义_routerRoot
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

  Object.defineProperty(Vue.prototype, '$router', { //在Vue原型上定义,可以直接访问:this.$router
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', { //在Vue原型上定义,可以直接访问:this.$route
    get () { return this._routerRoot._route }
  })
 // 定义了Vue全局的组件
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

//最后设置路由组件的`beforeRouteEnter`、`beforeRouteLeave`、`beforeRouteUpdate`守卫的合并策略
  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}

VueRouter 构造函数

export default class VueRouter {
...
constructor (options: RouterOptions = {}) {
....
//createMatcher通过对我们传入routes做一定的处理,将嵌套的结构处理成为一个较为容易操作的数据结构,维护成数组或是对象
 this.matcher = createMatcher(options.routes || [], this)
 // 图1 为这个函数的返回值 =》 this.matcher
 // 图2 this.matcher.getRoutes() 可以获取当前的路由参数,就是我们项目中配置的那个数组
 // 图3  this.matcher.match('/')传入一个路径可以拿到的东西,就是我饿么this.$route拿到的数据结构一样的,本质就是match方法生成的

// mode 选择路由的模式
  let mode = options.mode || 'hash'
  this.fallback =
    mode === 'history' && !supportsPushState && options.fallback !== false
  if (this.fallback) {
    mode = 'hash'
  }
  if (!inBrowser) {
    mode = 'abstract'
  }
  this.mode = mode

// 无论是HTML5History 和 HashHistory都会继承 ‘History’,这个History的核心方法就是`TransitionTo`
  switch (mode) {
    case 'history':
      this.history = new HTML5History(this, options.base)
      break
    case 'hash':
      this.history = new HashHistory(this, options.base, this.fallback)
      break
    case 'abstract':
      this.history = new AbstractHistory(this, options.base)
      break
    default:
      if (process.env.NODE_ENV !== 'production') {
        assert(false, `invalid mode: ${mode}`)
      }
  }
}

图1 image.png

图2 image.png

图3

image.png

TransitionTo主要做了两件事

  • 更新当前的路由对象:updateRoute
  • 触发响应式
  • 执行传入的onComplete方法:confirmTransition

router-link[vue-router\src\components\link.js]

image.png 原来有这么多参数可以用,一般我们只会在项目里面用到to

router-view[vue-router\src\components\view.js]

主要就是匹配‘name’的组件用于渲染出来,但是由于路由可以多层,所以需要深度匹配渲染

image.png

下面列举一下vue-router相关的面试题,可以一个个结合源码看一下

vue-router怎么重定向的?alias是什么

redirect表示url输入原来的地址,但是会被重定向到recirect配置的地址

image.png

image.png

alias别名

 { path: '/', component: A, alias: '/aaa' } 路由上会显示/b但是还是访问的A组件

image.png

vue-router 是什么? 它有哪些组件

这个问题可以根据上方探讨的源码进行溯源。 拥有的组件:

router-link

router-view

后记

本文仅作为自己一个阅读记录,具体还是要看大佬们的文章

下一篇:我的源码学习之路(12)---延展vue路由