vue2 vue-router源码解析

目录

Vue Router 的基本结构和功能

源码分析

一. 编写install 方法

二 .生命变量存储路由信息和当前路由

三 .初始化路由 把路由信息记录在routeMap中

 四.注册router-link 和router-view 组件


Vue Router 的基本结构和功能

  1. 路由器实例(Router 实例):Vue Router 提供了一个 VueRouter 类,用于创建路由器实例。路由器实例通常通过 new VueRouter() 创建,并通过 Vue 实例的 router 选项进行注册。

  2. 路由器插件(Router 插件):Vue Router 提供了一个 install 方法,使其可以作为 Vue.js 插件使用。通过在 Vue 实例上调用 Vue.use(VueRouter),可以在应用程序中全局注册路由器。

  3. 路由表(Route Table):路由表定义了 URL 和组件之间的映射关系。它是一个包含路由配置的 JavaScript 对象或数组,每个路由配置项都定义了一个 URL 匹配规则和对应的组件。

  4. 路由模式(Router Mode):Vue Router 支持多种路由模式,包括 hash 模式、history 模式和 abstract 模式。这些模式决定了 URL 如何与路由器进行交互。

  5. 路由导航(Route Navigation):Vue Router 提供了一组导航方法,用于在不同的 URL 之间进行导航。它包括 router.push()router.replace()router.go() 等方法,以及 <router-link> 组件用于声明式的导航。

  6. 导航守卫(Navigation Guards):Vue Router 提供了一组导航守卫,用于在路由导航过程中执行特定的逻辑。导航守卫包括全局前置守卫、路由独享守卫、组件内的守卫等。

  7. 动态路由和嵌套路由(Dynamic Routing and Nested Routing):Vue Router 支持动态路由和嵌套路由,允许在 URL 中包含动态参数,并且可以在组件中进行嵌套路由的声明。

  8. 路由状态管理(Router State Management):Vue Router 允许在路由器实例中定义和管理全局的路由状态,并通过 $route 对象和 $router 实例提供了访问和修改路由状态的方法。

源码分析

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

由于在使用Router时 使用了Vue.use 并且Router为一个对象所以 Router里有一个install方法

路由的本质就是地址栏的切换渲染不同的内容

首先我们先新建一个vueRouter文件夹 在这个文件夹下新建一个index.js文件 我们对外暴露一个名字为Router的class 然后在里面写一个install方法

export default class  Router{
    static install(){
            
    }
}

一. 编写install 方法

install 方法是默认就加载的我们把初始化逻辑在这里写

第一步判断是否注册过插件 用变量installed来判断

第二步 把vue构造函数记录到全局变量

第三步 混入

路由在每个页面都可以使用 原因就是源码中使用了混入mixin 在beforeCreate生命周期中在每个组件实例创建之前,将路由配置添加到了组件实例中

let _Vue = null;
export default class Router {
  static install(Vue) {
    //判断是否注册过插件
    if (Router.install.installed) {
      return;
    }
    Router.install.installed = true;
    //把vue构造函数记录到全局变量
    _Vue = Vue;
    //混入
    _Vue.mixin({
      beforeCreate() {
        //这里的this.$options是在Vue 2中,this.$options对象包含了创建Vue实例时传递的选项。
        //当你在Vue实例中使用router选项来配置Vue Router时,它会被保存在this.$options对象中
        if (this.$options.router) {
          //把router挂在到_Vue原型上
          _Vue.prototype.$router = this.$options.router;
        }
      },
    });
  }
}

二 .生命变量存储路由信息和当前路由

 constructor(options){
    //记录options信息
    this.options = options
    //存储路由信息
    this.routeMap = {}
    //记录当前的路由 默认是/
    this.data = _Vue.observable({
        current:'/'
    })
 }

三 .初始化路由 把路由信息记录在routeMap中

 init(){
     //初始话路由
    this.createRouteMap(this.options.routes)
 }
 //这个方法里涉及到嵌套路由的遍历
 createRouteMap(routes){
    //遍历所有的路由规则包括嵌套路由 存储到routeMap中
    routes.forEach(route=>{
    // 判断当前路由是否有path和component
    if (route.path && route.component) {
        // 将path和component作为键值对存储到routeMap中
        this.routeMap[route.path] = route.component;
      }
      // 判断当前路由是否有子路由
      if (route.children && route.children.length > 0) {
        // 递归遍历子路由
        this.createRouteMap(route.children);
      }
    }) 
 }

 四.注册router-link 和router-view 组件

router-link 的实质就是一个a标签,我们需要组织默认行为.router-link 跳转的时候是根据to传的参数进行的,参数可以是String或者Object类型 我们进行如下写操作 :

 //注册组件
 initCompontent(Vue){
    Vue.component('router-link',{
        props:{
            to:[String,Object]
        },
        render(h){
            return h('a',{
                attrs:{
                    href:this.resolveTo
                },
                on:{
                    click:this.clickHandler
                },
            },[this.$slots.default])
        },
        computed: {
            resolveTo() {
              // 判断传入的to是否是对象
              if (typeof this.to === 'object') {
                  //这里也可以返回path自行判断
                return this.to.name
              } else {
                // to不是对象,直接使用to作为href
                return this.to;
              }
            },
          },
        methods:{
            clickHandler(e){
                //阻止默认事件
                e.preventDefault()
                if (typeof this.to === 'object') {
                    for (let key in this.to) {
                        if (this.to.hasOwnProperty(key)) {
                            //这里我默认写的是name去匹配 实际逻辑要多一点 
                          history.pushState({}, '', this.to.name + this.to[key] );
                          this.$router.data.current = this.to.name;
                            return
                        }
                      }
                  }
                  //利用history.pushState去改变地址栏的路由 无刷新地向当前history插入一条历使状态
                 history.pushState({},'',this.to)
                 //渲染新的组件
                 this.$router.data.current = this.to  
            }
        }
    })
 }

这时候在使用router-link 进行跳转时是没有问题的,但是当点击浏览器后退或者前进时,地址栏路径变了,页面却没有渲染出来

原因就是对应的页面没有update,我们还需要监听浏览器的popstate事件

新增方法initEvent 监听popstate事件,然后从新更新页面

 initEvent(){
    window.addEventListener('popstate',()=>{
        //监听popstate事件 window.location.pathname获取路由地址复制给current从新渲染新的页面
        this.data.current = window.location.pathname
    })
 }

 在init里调用事件

 //初始化
 init(){
    this.createRouteMap(this.options.routes)
    this.initCompontent(_Vue)
    this.initEvent()
 }

在initCompontent方法里面注册router-view组件

 //注册组件
 initCompontent(Vue){
    Vue.component('router-link',{
        props:{
            to:[String,Object]
        },
        render(h){
            return h('a',{
                attrs:{
                    href:this.resolveTo
                },
                on:{
                    click:this.clickHandler
                },
            },[this.$slots.default])
        },
        computed: {
            resolveTo() {
              // 判断传入的to是否是对象
              if (typeof this.to === 'object') {
                return this.to.name
              } else {
                // to不是对象,直接使用to作为href
                return this.to;
              }
            },
          },
        methods:{
            clickHandler(e){
                //阻止默认事件
                e.preventDefault()
                if (typeof this.to === 'object') {
                    for (let key in this.to) {
                        if (this.to.hasOwnProperty(key)) {
                          history.pushState({}, '', this.to.name + this.to[key] );
                          this.$router.data.current = this.to.name;
                            return
                        }
                      }
                  }
                 history.pushState({},'',this.to)
                 this.$router.data.current = this.to  
            }
        }
    })

    let self = this
    Vue.component('router-view',{
        render (h){
            /这里只是一个简易版本 当有多个router-view时候会出问题
            const compontent = self.routeMap[self.data.current]
            return h(compontent)
        }
    })
 }

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值