介绍
本文目的是实现一个简易版本的VueRouter用以学习,也是对从网课学习的总结和复习。其中内容仅为hash模式下的简易实现,且未处理嵌套路由和路由守卫等情况,多有不足之处,请多多交流。
内容拆分
要实现的简单版VueRouter主要分为两大部分:
-
主体Router类
- 保存构造函数的传入的参数
- 获取当前路由的路径并设置监听事件
-
install方法
- 保存参数中Vue的构造方法
- 采用mixin混入方式挂载$router
- 注册全局组件:router-view、router-link
插件测试
先修改插件的引入信息以供实现过程中输出信息查看,也便于发现错误问题。
- 复制router文件夹并修改main.js中引入路径
// 修改前
import router from "./router";
// 修改后:复制后的文件夹重命名为krouter
import router from "./krouter";
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
- 创建kvue-router.js并修改index.js中引入路径
// 修改前
import VueRouter from "vue-router";
// 修改后
import VueRouter from "./kvue-router";
到此,就修改好了,可以开始插件的实现了。
插件实现
架构搭建
首先搭建一个插件的架子,根据上文的内容拆分
- 主体Router类
- install方法
可以得到如下代码:
class Router{}
Router.install = ()=>{}
export default Router
install方法实现
在install方法中,主要任务是
- 保存参数中Vue的构造方法
- 采用mixin混入方式挂载$router
- 注册全局组件:router-view、router-link
保存Vue构造方法
在install方法中,传入的参数为Vue的构造方法。
具体原因是在router/index.js中所执行的 Vue.us(VueRouter) 中调用了install方法,使得Vue构造方法可以以参数形式传入install中。
这也是Vue插件的一个固定写法,install中通过保存参数为全局变量的做法可以在不引入Vue的情况下使用Vue的方法,既减少了对Vue的依赖也缩小了插件体积。
let Vue;
Router.install = (_Vue)=>{
Vue = _Vue;
}
挂载$router
这里采用混入方式的原因是Vue.use(VueRouter)执行的时候通过 new Vue({}) 创建的Vue实例还没有执行,但是挂载时候需要从Vue实例中获取到router数据,所以采用混入的方式可以将挂载的时机延后至Vue实例创建完毕。
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
})
创建全局组件
VueRouter中的全局组件有两个:router-link和router-view。
在实现组件之前,我们需要知道一个事情:通过Vue-Cli生成的项目环境是基于Webpack的运行时版本,不包含编译器。如果在创建组件时我们通过template模板方式,则只能够在运行时环境使用,无法执行编译,所以只能够通过render渲染函数的方式来实现。
router-link
router-link标签有一个to属性,是必须有的。而且router-link标签内部可以有其他内容,所以需要通过插槽获取内部内容。
// krouter-link.js
export default {
props: {
to: {
type: String,
required: true
}
},
render(h) {
return h('a', { attrs: { href: "#" + this.to } }, this.$slots.default)
},
}
//kvue-router.js
import Link from './krouter-link.js';
Vue.component('router-link', Link);
router-view
router-view组件的实现需要Router类中数据的配合,所以留后待述。
Router类实现
在Router类中,主要任务是:
- 保存构造函数的传入的参数
- 获取当前路由的路径并设置监听事件
保存构造函数参数
在Router类的构造函数中,参数为router/index.js中的routes等信息。
class Router {
constructor(options) {
this.$options = options;
}
}
获取当前路由并设置监听事件
本次仅实现hash模式,history模式留待后续。
从地址栏获取到当前路由的path路径并将其保存为响应式数据。对 hashchange 和 load 事件设置监听。
class Router {
constructor(options) {
this.$options = options;
// 获取当前路由路径
const current = window.location.hash.slice(1) || '/';
// 该方法是Vue实例的工具方法,作用是给this添加一个响应式属性current,并附上初值;
// current只有是响应式数据,才能在值变化时促使render函数重新执行,组件重新渲染页面
Vue.util.defineReactive(this, 'current', current);
window.addEventListener('hashchange', this.onHashChange.bind(this));
window.addEventListener('load', this.onHashChange.bind(this));
}
onHashChange() {
this.current = window.location.hash.slice(1);
}
}
至此,主体Router类实现完毕,可以开始补充上文未实现的router-view组件
补充router-view组件实现
router-view组件中需要根据current信息在routes中遍历查找相匹配的路由信息,并从中获取组件配置对象。
export default {
render(h) {
let component = null;
let route = this.$router.$options.routes.find(route => route.path === this.$router.current);
if (route) {
component = route.component;
}
return h(component)
},
}
至此,可以在页面中点击查看测试效果。