vue的路由的原理(自己封装一个vue-router插件)

news/2024/11/7 23:52:37/

vue的路由的原理

  • 前言:
    • 路由实例化:
    • 路由匹配:
    • 路由跳转:
    • 路由钩子:
    • 插件调用install方法
    • 封装RouterView
    • 封装RuoterLink
    • 详细步骤
      • main.js
      • \src\router\index.js
      • \src\plugins\router.js
      • \src\plugins\components\RouterView.js
      • \src\plugins\components\RouterLink.js

前言:

  • Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,可以非常方便地实现单页应用程序的路由功能
  • 两种模式:hash模式和history模式
  • hash模式实现基础步骤:
    • vueRouter作为一个插件 ,需要实现install方法,install方法中接收两个参数,一个是vue构造函数,另一个是使用**vue.use()**的时候传递的参数。
    • 然后我们去监听onHashchange事件的变化,当hash值发生变化时,匹配对应的组件。这个时候我们需要封装一个match方法,循环routes(routes当中存放组件和路径的对应关系),拿到所有组件和路径
    • 定义一个响应式对象,代表当前路径,判断当前路径和循环的路径是否相等,并且是否包含,如果满足条件就push到数组当中
      所有的组件都存到数组中去,onHashchange发生变化时,把组件都清空,在继续调用match方法
    • 这个时候进行router-view,对被routerView渲染的组件进行标记为true
    • 在app组件中被routerView渲染机组件层级初始值标记为0
    • 找到当前组件的父组件
    • 对父组件进行循环,知道找到app组件,再循环中判断当前的子组件是否为routerView组件,如果是,层级**+1**
    • 定义存储组件的变量,找到对应组件赋值

路由实例化:

Vue Router 的核心是一个 Vue 插件,通过调用 Vue.use(VueRouter) 方法来实例化一个路由对象。在 VueRouter 的构造函数中,会初始化一些基本属性和方法,如 options、history、current、match 等。

 constructor(options) {this.$options = options;Vue.util.defineReactive(this, 'matched', [])const url = location.hash.slice(1) || '/'// 定义一个响应式的current属性Vue.util.defineReactive(this, 'current', url);this.init();}// 初始化函数init() {this.match();this.bindEvent();}

路由匹配:

Vue Router 的路由匹配是通过 match 方法实现的。match 方法接收一个 route 对象,返回一个匹配的路由记录。在 match 方法中,会遍历路由表,根据路由表中的 path 和 route.path 进行匹配,如果匹配成功,则返回对应的路由记录。
bindEvent方法绑定hashchange事件,监听hash值的变化

match(routes) {routes = routes || this.$options.routes;routes.forEach(route => {// 处理路由完全匹配(根路径)if(route.path === '/' && this.current.includes(route.path)) {this.matched.push(route)}// /course/vueif(route.path !== '/' && this.current.includes(route.path)) {this.matched.push(route);if(route.children) {this.match(route.children)}}})}// 绑定hashchange事件,监听hash的变化bindEvent() {// 监听hash的变化addEventListener('hashchange', () => {// 当hash值改变时,将最新的hash值赋值给currentthis.current = location.hash.slice(1) || '/'// 清空路由数组this.matched = [];this.match();})}

路由跳转:

Vue Router 的路由跳转是通过 push、replace、go、back 等方法实现的。在这些方法中,会调用 history 对象的 pushState、replaceState、go、back 等方法,实现浏览器的前进后退和地址栏的变化。

路由钩子:

Vue Router 的路由钩子是通过 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 等方法实现的。在这些方法中,可以进行路由拦截、权限控制、数据预取等操作。

 Vue.mixin({beforeCreate() {if(this.$options.router) {}}})

插件调用install方法

VRouter.install = function(_Vue) {Vue = _Vue;Vue.mixin({beforeCreate() {if(this.$options.router) {// 这样做就可以通过组件的实例访问到router对象Vue.prototype.$router = this.$options.router;}}})// 注册router-link和router-view两个全局组件Vue.component('router-link', RouterLink)Vue.component('router-view', RouterView)
}

封装RouterView

  • 对被routerView渲染的组件进行标记为true
  • 在app组件中被routerView渲染机组件层级初始值标记为0
  • 找到当前组件的父组件
  • 对父组件进行循环,知道找到app组件,再循环中判断当前的子组件是否为routerView组件,如果是,层级**+1**
  • 定义存储组件的变量,找到对应组件赋值
export default {render(h) {// 对当前的routerView组件进行标记(即被routerView渲染的组件)this.$vnode.data.routerView = true;// 路由嵌套层级计算(在App组件中被routerView渲染的组件层级为0)let depath = 0;// 找到当前组件的父组件(在App组件中被routerView渲染的组件的父组件就是根组件App)let parent = this.$parent;// 循环父组件,一直找到App组件退出循环while(parent) {// 一直往下找子组件const vnodeData = parent.$vnode && parent.$vnode.data;if(vnodeData) {// 判断当前子组件是否是routerView组件,如果是让其层级+1if(vnodeData.routerView) {depath++;}}parent = parent.$parent;}// 定义存储组件的变量let componet = null;// 找到对应组件赋值const route = this.$router.matched[depath];if(route) {componet = route.component;}return h(componet)}
}

封装RuoterLink

export default {props: {to: {type: String | Object,required: true}},render(h) {return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)}
}

详细步骤

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'Vue.config.productionTip = falsenew Vue({render: h => h(App),router
}).$mount('#app')

\src\router\index.js

import Vue from 'vue';
import VueRouter from '../plugins/router'Vue.use(VueRouter)const router = new VueRouter({routes: [{path: '/home',component: () => import('../components/Home')},{path: '/cate',component: () => import('../components/Cate')},{path: '/course',component: () => import('../components/Course'),children: [{path: '/course/vue',component: () => import('../components/course/VueCourse')},{path: '/course/react',component: () => import('../components/course/ReactCourse')}]}]
})export default router;

\src\plugins\router.js

import RouterLink from './components/RouterLink'
import RouterView from './components/RouterView'let Vue;
class VRouter {constructor(options) {this.$options = options;Vue.util.defineReactive(this, 'matched', [])const url = location.hash.slice(1) || '/'// 定义一个响应式的current属性Vue.util.defineReactive(this, 'current', url);this.init();}// 初始化函数init() {this.match();this.bindEvent();}match(routes) {routes = routes || this.$options.routes;routes.forEach(route => {// 处理路由完全匹配(根路径)if(route.path === '/' && this.current.includes(route.path)) {this.matched.push(route)}// /course/vueif(route.path !== '/' && this.current.includes(route.path)) {this.matched.push(route);if(route.children) {this.match(route.children)}}})// console.log(this.matched);}// 绑定hashchange事件,监听hash的变化bindEvent() {// 监听hash的变化addEventListener('hashchange', () => {// 当hash值改变时,将最新的hash值赋值给currentthis.current = location.hash.slice(1) || '/'// 清空路由数组this.matched = [];this.match();})}
}VRouter.install = function(_Vue) {Vue = _Vue;Vue.mixin({beforeCreate() {if(this.$options.router) {// 这样做就可以通过组件的实例访问到router对象Vue.prototype.$router = this.$options.router;}}})// 注册router-link和router-view两个全局组件Vue.component('router-link', RouterLink)Vue.component('router-view', RouterView)
}export default VRouter;

\src\plugins\components\RouterView.js

export default {render(h) {// 对当前的routerView组件进行标记(即被routerView渲染的组件)this.$vnode.data.routerView = true;// 路由嵌套层级计算(在App组件中被routerView渲染的组件层级为0)let depath = 0;// 找到当前组件的父组件(在App组件中被routerView渲染的组件的父组件就是根组件App)let parent = this.$parent;// 循环父组件,一直找到App组件退出循环while(parent) {// 一直往下找子组件const vnodeData = parent.$vnode && parent.$vnode.data;if(vnodeData) {// 判断当前子组件是否是routerView组件,如果是让其层级+1if(vnodeData.routerView) {depath++;}}parent = parent.$parent;}// 定义存储组件的变量let componet = null;// 找到对应组件赋值const route = this.$router.matched[depath];if(route) {componet = route.component;}return h(componet)}
}

\src\plugins\components\RouterLink.js

export default {props: {to: {type: String | Object,required: true}},render(h) {return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)}
}

http://www.ppmy.cn/news/74408.html

相关文章

八.异常控制流ECF

异常 类别原因异步/同步返回行为示意图中断来自IO设备的信号异步下一条指令陷阱有意的异常同步下一条指令,如syscall(系统调用)故障潜在可恢复的错误同步可能返回到当前指令终止不可恢复的错误同步不会返回 故障中的“除法错误”、“一般保护故障”会直接终止&…

用Powerpoint (PPT)制作并导出矢量图、高分辨率图

论文写作时经常需要导入矢量图,正规军都是用AI或者Inkscape作图,但是PPT更加适合小白用户,或者一些简单的构图需求使用PPT更加便捷,而且不得不承认PPT的某些功能是真的香,例如:简单的对齐、文字插入和格式修…

[网鼎杯 2020 青龙组]jocker 题解

32位无壳 堆栈有问题 先修堆栈在反编译 查看关键函数 对输入的字符串进行了加密 加密之后omg函数中与存储的字符串进行比较 我们先解密这个 提取数据 解密脚本 data[0x66,0x6b,0x63,0x64,0x7f,0x61,0x67,0x64,0x3b,0x56,0x6b,0x61,0x7b,0x26,0x3b,0x50,0x63,0x5f,0x4d,0x5…

ubuntu18安装中文环境

如果你在Ubuntu 18.04安装过程中选择了中文语言环境,但是在启动后却出现了英文界面,可能是因为Ubuntu的语言设置没有被正确识别。你可以尝试以下方法来解决这个问题: 修改语言环境设置: 打开终端(Terminal&#xff0…

想自学写个操作系统,有哪些推荐看的书籍?

前言 哈喽,我是子牙,一个很卷的硬核男人。喜欢研究底层,聚焦做那些大家想学没地方学的课程:手写操作系统、手写虚拟机、手写编程语言… 今天我们将站在一个自学者的角度来聊聊如何实现自己的操作系统。并为大家推荐几本能够帮助你…

【数据结构】KMP算法:计算next与nextval函数值(图解)

例&#xff1a;计算模式串"abaabcac"的KMP算法中next函数值 由函数定义 n e x t [ j ] { 0 , j 1 M a x { k ∣ 1 < k < j 且 " t 1 t 2 ⋅ ⋅ ⋅ t k − 1 " " t j − k 1 t j − k 2 ⋅ ⋅ ⋅ t j − 1 " } 1 , k 1 next[j]\left…

2023新版Spring6全新讲解-SpringFramework介绍

SpringFramework介绍 一、官网地址阅读 https://spring.io/ 二、Spring的发展历史 三、Spring的概述 一个Java应用层程序&#xff0c;是由许多个类组成的&#xff0c;这些类之间必然存在依赖关系&#xff0c;当项目越来越大&#xff0c;依赖关系越来越复杂&#xff0c;需要一…

使用JavaScript+Selenium玩转Web应用自动化测试

自动化测试 在软件开发过程中, 测试是功能验收的必要过程, 这个过程往往有测试人员参与, 提前编写测试用例, 然后再手动对测试用例进行测试, 测试用例都通过之后则可以认为该功能通过验收. 但是软件中多个功能之间往往存在关联或依赖关系, 某一个功能的新增或修改可能或影响到其…