手写vue-router核心源码

本文详细解析了vue-router的使用规则、基本结构、核心代码,包括创建路由映射表、监听浏览器URL变化、两种模式(history和hash)、安装时的init方法以及router-link和router-view组件的实现。此外,还介绍了vue-router的钩子函数和设计优点,如代码复用、路由匹配和组件渲染。
摘要由CSDN通过智能技术生成

1、vue-router使用规则

vue-router使用时 要调用Vue.use(VueRouter)
使用如下:

router/index.js

import VueRouter from '@/vue-router';
Vue.use(VueRouter);

const routes = [
    {
   
        path: '/',
        name: 'Home',
        component: Home
    },
]
const router = new VueRouter({
   
    mode: 'history',
    base: process.env.BASE_URL,
    routes
});

export default router;


// main.js
import router from './router'

Vue.config.productionTip = false

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

Vue.use函数式怎样的?
看源码

// Vue.use源码

// Vue源码文件路径:src/core/global-api/use.js

import {
    toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
   
  Vue.use = function (plugin: Function | Object) {
   
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
   
      return this
    }

    // additional parameters
    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
   
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
   
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

从源码中我们可以发现vue首先判断这个插件是否被注册过,不允许重复注册。
并且接收的plugin参数的限制是Function | Object两种类型。
对于这两种类型有不同的处理。
首先将我们传入的参数整理成数组 => const args = toArray(arguments, 1)。

// Vue源码文件路径:src/core/shared/util.js
export function toArray (list: any, start?: number): Array<any> {
   
  start = start || 0
  let i = list.length - start
  const ret: Array<any> = new Array(i)
  while (i--) {
   
    ret[i] = list[i + start]
  }
  return ret
}

再将Vue对象添加到这个数组的起始位置args.unshift(this),这里的this 指向Vue对象
如果我们传入的plugin(Vue.use的第一个参数)的install是一个方法。也就是说如果我们传入一个对象,对象中包含install方法,那么我们就调用这个plugin的install方法并将整理好的数组当成参数传入install方法中。 => plugin.install.apply(plugin, args)
如果我们传入的plugin就是一个函数,那么我们就直接调用这个函数并将整理好的数组当成参数传入。 => plugin.apply(null, args)

以上可知 plugin.install函数第一个参数就是Vue参数,后面的参数才是传入参数
Vue.use(plugin)目的是为了执行plugin里面的install方法 并且传入Vue对象, 同时vue和vue-router中 vue版本和用户使用保持了一致性

2、编写vueRouter基本结构

const router = new VueRouter({
   
    mode: 'history',
    base: process.env.BASE_URL,
    routes
});

2.1 上面使用可知,VueRouter是一个构造函数并且接受对象传参, 因此编写构造函数和init函数,传入的options就是我们new VueRouter参数对象, 拿到routes 要生成一个路径和组件的映射表,为了做组件渲染用。

// vue-router/index.js

import {
    install } from './install';
import {
    createMatcher } from './create-matcher';
import HashHistory from './history/hash';
import BrowserHistory from './history/history';	

export default class VueRouter {
   
    constructor(options) {
   
    	// 根据用户的配置 生成一个映射表 稍后跳转时 根据路径找到对应组件来进行渲染
        // 创建匹配器后,核心的方法就是匹配
        // match addRoutes
        this.matcher = createMatcher(options.routes || []);
        // 定义其中一个钩子函数队列, 其实有很多 先忽略其他
        this.beforeEachHooks = [];
        // 根据当前的mode 创建不同的history管理策略
        switch (options.mode) {
   
            case 'hash': {
   
                this.history = new HashHistory(this);
                break;
            }
            case 'history': {
   
                this.history = new BrowserHistory(this);
                break;
            }
        }
    }
    init(app) {
   
        // app根实例
        // 路由初始化
        console.log('init');
        
    }
}
VueRouter.install = install;

2.2 并且要符合Vue.use(VueRouter), 故VueRouter要增加install的函数, 稍后再讲解createMatcher、HashHistory、BrowserHistory。

2.3定义install函数
我需要将当前的根实例的提供的router属性 共享给所有子组件 Vue.prototype不好 会影响了全局的Vue实例 改用mixin

/*
 * @description:
 * @author: steve.deng
 * @Date: 2020-12-22 07:19:31
 * @LastEditors: steve.deng
 * @LastEditTime: 2020-12-23 15:19:15
 */
// install.js
export let _Vue;

// 直接使用传入的vue  不需要引入了 版本也保持一直
export function install(Vue, options) {
   
    _Vue = Vue;

    //我需要将当前的根实例的提供的router属性 共享给所有子组件 Vue.prototype不好 会影响了全局的Vue实例 改用mixin

    // 所有子组件初始化的时候 都会去调用
    Vue.mixin({
   
        beforeCreate() {
   
            // 获取到每个人的实例 给实例添加属性
            console.log(this.$options);
            // 共用顶层this
            if (this.$options.router) {
   
                this._routerRoot = this; // 把根实例挂载到_routerRoot上
                this._router = this.$options.router;
                this._router.init(this);
           
                console.log('this._route', this._route);
                // this._router 路由实例
            } else {
   
                // this._routerRoot 指向当前根组件实例
                this._routerRoot = this.$parent && this.$parent
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值