先来简单回复下VueRouter的简单使用
npm install @vue/cli -g
vue create vue-router
Babel 因为代码可能会用到.vue组件,babel会通过vue-loader对.vue组件进行编译
打开项目/src/router/index.js文件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
可以看到通过Vue.use(VueRouter)来使用VueRouter的。
Vue.use的核心原理
// 伪代码
Vue.use = function(plugin, options) {
plugin.install(this, options)
}
插件会默认调用install方法,第一个参数this指的Vue的构造函数,第二个参数指的是Vue的一个选项。这样写的原因是为了让项目中的Vue版本和VueRouter中的项目版本一致。
比如vue-router中引入的Vue2.0,项目中Vue的版本是3.0这样就可能出现不兼容的问题。
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
使用new方法调用VueRouter说明VueRouter是一个类。
前端路由实现一般就两种模式 1.hash 2.history
就可以监听hash的变化,来控制组件的显示。
但是这种方案比较丑,但兼容性好
我们实际在页面中访问的路径一般
window.history.pushState({}, null, 'xxx')
但是这样路径并不存在,当我们直接在当前页面刷新就会去服务器请求这个页面,但服务器并没有这个页面,找不到这个页面,所以需要服务端支持。项目里因为用了webpack,webpack中有一个插件history-fallback支持这种跳转。
两种模式都不会像服务器发起真实请求。
#/ 这个是hash #xxx这个是锚点
/src/vue-rouer/index.js
export default class VueRouter {}
function install(Vue, options) {
console.log(Vue, options)
}
VueRouter.install = install
控制台输出

/src/router/index.js
import Vue from 'vue'
import VueRouter from '@/vue-router'
// ...
Vue.use(VueRouter) // 为了使vue-router中的Vue的版本和用户使用的一致
// ...
export default router
页面输出
这里插入一个小的知识点
export a
settimeinterval(() => {
a++
}, 1000)
import { a } from './vue'
settimeinterval(() => {
a++
}, 1000)
a会变吗?
下面我们来分析install中到底做了哪些事?
当我们在如果你要使用到router,你会在实例化Vue的参数options中加入router
src/main.js
new Vue({
router, // 创建实例时提供router属性,表示初始化路由
render: h => h(App)
}).$mount('#app')
我们给Vue根实例注入了router属性,所有页面都可以拿到这个属性
install方法的实现
export let _Vue;
export default function install(Vue) {
_Vue = Vue;
Vue.mixin({ // 给所有组件的生命周期都增加beforeCreate方法
beforeCreate() {
if (this.$options.router) { // 如果有router属性说明是根实例
// 这里的this指向的是根组件
this._routerRoot = this; // 将根实例挂载在_routerRoot属性上
this._router = this.$options.router; // 获取options里面的router配置赋值给根实例的_router属性,也就是VueRouter的实例
this._router.init(this); // 初始化路由,这里的this指向的是根实例
} else { // 父组件渲染后会渲染子组件
// 这里的this指向的当前渲染的组件,当前组件通过$parent找到父组件的_routerRoot属性,由于在上面的if语句中我们已经将根实例赋值给了_routerRoot属性,并将router配置赋值给了根组件的_router属性,所以可以通过$parent._routerRoot获取到根组件,并通过根组件拿到router配置项,再将这个属性赋值给当前组件的_routerRoot属性,当再渲染其他组件的时候就可以一层层获取到router配置了
this._routerRoot = this.$parent && this.$parent._routerRoot;
}
}
})
}
需要将根组件的router赋值子组件,让子组件也能拿到router属性
渲染的时候先渲染根组件再依次渲染其他组件,组件是一层层渲染的
注意install方法中的this指的当前的组件
这里我们应该在Vue-Router上增加一个init方法,主要目的就是初始化功能
这里在强调下,什么是路由? 路由就是匹配到对应路径显示对应的组件!
import createMatcher from './create-matcher'
import install from './install'
export default class VueRouter{
constructor(options){
// 根据用户传递的routes创建匹配关系,this.matcher需要提供两个方法
// match:match方法用来匹配规则
// addRoutes:用来动态添加路由
this.matcher = createMatcher(options.routes || []);
}
init(app){}
}
VueRouter.install = install;
编写createMatcher方法
import createRouteMap from './create-route-map'
export default function createMatcher(routes) {
// 收集所有的路由路径, 收集路径的对应渲染关系
// pathList = ['/','/about','/about/a','/about/b']
// pathMap = {'/':'/的记录','/about':'/about记录'...}
let {pathList,pathMap} = createRouteMap(routes);
// 这个方法就是动态加载路由的方法
function addRoutes(routes){
// 将新增的路由追加到pathList和pathMap中
createRouteMap(routes,pathList,pathMap);
}
function match(){} // 稍后根据路径找到对应的记录
return {
addRoutes,
match
}
}
这里需要创建映射关系,需要createRouteMap方法
路由的核心原则就是根据路径返回对应的组件