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)}
}