vue-router的源码实现过程

lxf2023-05-07 00:53:36

一、路由更新的原理图:

前三步是vue-router实现的,后四步是vue实现的

graph TD
url改变 --> 触发监听事件 --> 改变vue-router里面的current变量 --> 改变vue中_route变量 --> vue监听_route的变化  --> 获取到最新的组件 -->  render新组件

二、main.js 入口文件

初始化,把router实例挂载到vue的根实例上, main.js 中是根实例,App.vue 子实例, 其余的是孙子实例

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  name: 'main',
  router,
  render: h => h(App)
}).$mount('#app')

三、router / index.js

作用:(1)vue.use 注册插件 (2)router实例化过程

import Vue from 'vue'
import VueRouter from '../vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
console.log('router.js');
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About,
    children: [
      {
        path: 'a',
        name: 'AboutA',
        component: {
          render() {
            return <h1>about a</h1>
          }
        }
      },
      {
        path: 'b',
        name: 'AboutB',
        component: {
          render() {
            return <h1>about b</h1>
          }
        }
      }
    ]
  }
]

const router = new VueRouter({
  routes
})

export default router
1、vue.use 注册插件,install.js

use的实现过程:

(1) 执行里面的方法

(2)如果有install这个属性 ,并且这个属性是个方法,不会执行方法本身,会执行install方法

(3)install这个方法的第一个参数是vue

vue-router的源码实现过程

vue-router的源码实现过程

install作用:先通过vue.mixin方法在生命周期钩子中混入注册属性、组件的方法,等vue实例化时,直接调用,注册全局属性和组件

(1)注册全局属性:routeroute、 router

(2)注册全局组件:router-view router-link

(3)初始化init: 根据当前路由,初始化页面,渲染对应组件,创建监听,后续路由改变时,页面改变

2、router实例化过程
const router = new VueRouter({
  routes
})
export default router

四、vue-router内部实现

vue-router的源码实现过程

router实例上的属性和方法:matcher、mode、history、init()、push()、replace()等等

1、matcher的作用

//嵌套路由数据
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About,
    children: [
      {
        path: 'a',
        name: 'AboutA',
        component: {
          render() {
            return <h1>about a</h1>
          }
        }
      },
      {
        path: 'b',
        name: 'AboutB',
        component: {
          render() {
            return <h1>about b</h1>
          }
        }
      }
    ]
  }
]


// 数据扁平化后

const pathList = ['/', '/about', '/about/a', '/about/b']  存储所有路径的数组
const pathMap = { 
    '/': '记录1',
    '/about': '记录2',
    '/about/a': '记录3',
    '/about/b': '记录4'
} 
record = {
   path: '/about/a',
   component: A组件, 
   parent: {
     .....记录
   }
}
// 路径的映射关系表

2、 init方法:根据当前路径显示到对应的页面

(1)渲染对应的组件

(2)创建监听,后续路由变化会重新跳转页面

(3)路由变化后,更改vue中对应的_route的值,触发响应式,从而页面更新 Vue.util.defineReactive(this, '_route', this._router.history.current)

3、create-matcher 内部实现:addRoutes方法、match方法

该方法是创建匹配器,根据不同的路由,匹配对应的组件,先把用户传入的路由信息扁平化,创建路由映射表,例如: /about/a

const pathList = ['/', '/about', '/about/a', '/about/b']  存储所有路径的数组
const pathMap = {  // 路径的映射关系表
    '/': '记录1',
    '/about': '记录2',
    '/about/a': '记录3',
    '/about/b': '记录4'
} 

其中pathList、pathMap是createRouteMap方法格式化的

(1)addRoutes方法:做权限管理时,需要动态添加路由

用途:接口返回来的动态路由信息,重新整合到映射表中(pathList、pathMap)

import createRouteMap from "./create-route-map"

export default function createMatcher(routes) {
    let {pathList, pathMap } = createRouteMap(routes)
    function addRoutes(routes) {
     // routes是新的路由信息
       createRouteMap(routes, pathList, pathMap)
    }
}
(2)match方法

找到对应的记录,并且要根据记录产生一个记录匹配数组,此处借助createRoute方法

 例如:/about/a路径时,需要先渲染/about父组件,再在父组件的router-view中渲染子组件a
 // pathMap = { '/about/a':  {path: '/about/a', component: a组件, parent: xx } }
 // record  = {
 //    path: '/about/a',
 //    component: A, 
 //    parent: {
 //      path: '/about',
 //      component: About,
 //      ...
 //     }
 // }
 
   function match(location) {
        let record = pathMap[location]
        let local = {
            path: location
        }
        if (record) { // 如果找到了当前的记录,会根据当前的记录产生一个route
            return createRoute(record, local)
        }  else {
            return createRoute(null, local)
        }
    }
 
    function createRoute(record, location) {
        // location ===> { path: "/about/a" }
      let res = []
      if (record) {
        while(record) {
            res.unshift(record)
            record = record.parent // 一直找父级,添到最前边
        }
      }
      return {
          ...location,
          matched: res
      }
    }
     // 最终生成的数据格式为:
     {
        path: '/about/a',
        matched: [record1, record2] // record1对应about的信息, record2对应aboutA
     }
 

所以match的最终目的是生成下图所示结构的数据

vue-router的源码实现过程

4、history内部方法

vue-router的源码实现过程

history路由系统内部分三个类:公共类、hash类、h5类

hash类:getCurrentLocation()、setupHashLister()

base类:current当前路径、transitionTo()、updateRoute()等

  // hash.js
  getCurrentLocation() {
    return getHash()
  }
  setupHashLister() { // 创建监听,路由变化后的需要做的
    window.addEventListener('hashchange', () => {
        this.transitionTo(getHash()) // 当路由变化后,需要跳转到当前hash对应的页面
    })
  }
// 在router初始化时,有个属性history,根据模式,生成相应的路由系统
if (this.mode === 'hash') {
   this.history = new HashHistory(this)
}
// init初始化时,调用了history的transitionTo方法

history.transitionTo(路径,设置监听的回调)
(1)transitionTo方法逻辑
  • 判断路径,跟当前的是否一致,不一致,更新路由
  • 回调函数,创建监听
    transtionTo(location, onComplete) {
      let route = this.router.match(location) // 根据当前路径找到要渲染的组件
      if (this.current.path === location && route.matched.length === this.current.matched.length) {
         return // 如果是相同路径,就不进行跳转了
     } else {
         // 更新路径
         this.updateRoute(route) // 只有_route更新了,才能触发响应式,视图才可以更新
     }
      onComplete && onComplete()
    }
    updateRoute(route) {
        this.current = route  // this指的的history类,但是vue中的_route还没改变
        this.cb && this.cb(route) // 路径变化会将最新路径传递给listen
    }
    listen(cb) {
        this.cb = cb // 先存储方法
    }
    
    之前在init中调的listen,存储的回调方法
    // 发布订阅模式
    //  history.listen((route) => {
    //      app._route = route // 主要目的是更改_route,视图才可以更新
    //  })
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!