1、官方Vue Router使用步骤
在使用官方Vue Router时,一般会有以下几个步骤:
- 引入Vue Router插件:
import VueRouter from 'vue-router' - 安装路由功能:
Vue.use(VueRouter) - 定义路由实例:包括路由模式,路由组件,路由规则......
- 将路由实例挂载到根实例上:
new Vue({router}).$mount('#app')
对于这个过程,有两个值得注意的问题:
Vue.use【Vue在使用其它插件时也需要执行Vue.use】的作用是什么?- 为什么要把Vue Router实例挂载到根实例上?
我们带着这两个问题,来实现自己的简版Vue Route,并从中找到答案。
2、简版Vue Router实现思路
为了实现自己的Vue Router【MyVueRouter】,我们先来理清一下简单的实现思路:
- 明确MyVueRouter是一个插件,即最后会以
Vue.use的方式安装 - routes选项解析:生成一个map,把路径【hash】和component映射起来
- 监控url上hash变化,响应hash变化,获取并渲染对应组件
- 实现两个全局组件:
<my-router-link>和<my-router-view>
3、实现简版Vue Router
// my-vue-router.js
let Vue
class MyVueRouter {
constructor(opts) {
this.$opts = opts
// 创建一个路由path和component的映射
this.routeMap = {}
// 将来当前路径current需要响应式,利用Vue响应式原理可以做到这一点
this.app = new Vue({
data: {
current: '/'
}
})
}
// MyVueRouter的初始化方法
init() {
// 绑定浏览器事件
this.bindEvents()
// 解析路由配置
this.createRouteMap(this.$opts)
// 全局注册my-router-link和my-router-view
this.initComponent()
}
bindEvents() {
// bind(this)确保this指向
// 路由初始化
window.addEventListener('load', this.onHashChange.bind(this))
// 路由跳转
window.addEventListener('hashchange', this.onHashChange.bind(this))
}
onHashChange() {
// http://localhost/#/home
this.app.current = window.location.hash.slice(1) || '/'
}
createRouteMap(opts) {
// routes: [
// { path: "/home", component: Home },
// { path: "/about", component: About },
// ]
opts.routes.forEach(item => {
// ['/home']: {path:'/home',component:Home}
this.routeMap[item.path] = item.component
})
console.log('this.routeMap', this.routeMap)
}
initComponent() {
Vue.component('my-router-link', {
props: {
to: {
type: String,
required: true
},
},
render(h) {
// <a :href="to">xxx</a>
// h()写法
return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)
// JSX写法
// return <a href={this.to}>{this.$slots.default}</a>
}
})
// hash改变 -> 通过onHashChange监听,从而赋值current -> current通过Vue具有响应性,它的变化会使得[my-router-view]重新执行render
Vue.component('my-router-view', {
// 箭头函数能保留this指向,这里指向MyVueRouter实例
render: h => {
const Comp = this.routeMap[this.app.current]
return h(Comp)
}
})
}
}
// 把MyVueRouter变为插件
// Vue.use会执行MyVueRouter的install方法,并把Vue作为参数传给MyVueRouter[对于其他Vue插件也是]
MyVueRouter.install = function (_Vue) {
Vue = _Vue
// 混入,扩展Vue
Vue.mixin({
beforeCreate() {
// this指向Vue根组件实例
// 通过判断是否有“myRouter”选项,确保是根组件时执一次,将MyVueRouter实例放到Vue原型,以后所有组件实例就均有$myRouter
if (this.$options.myRouter) {
Vue.prototype.$myRouter = this.$options.myRouter;
this.$options.myRouter.init();
}
}
})
}
export default MyVueRouter
4、测试自己的简版Vue Router
// router/test-my-router.js
import Vue from 'vue'
import MyVueRouter from 'my-vue-router'
import Home from '../views/Home'
import About from '../views/About'
// 安装插件
Vue.use(MyVueRouter);
export default new MyVueRouter({
routes: [
{ path: "/home", component: Home },
{ path: "/about", component: About },
]
});
// main.js
import Vue from 'vue'
import App from './App.vue'
import myRouter from './router/test-my-router'
Vue.config.productionTip = false
new Vue({
myRouter,
render: h => h(App)
}).$mount('#app')
// TestMyRouter.vue
<template>
<div>
<my-router-link to="/home">Go to Home</my-router-link>
<my-router-link to="/about">Go to About</my-router-link>
<my-router-view />
</div>
</template>