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/vue
if(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值赋值给current
this.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) {
}
}
})
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 {
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组件,如果是让其层级+1
if(vnodeData.routerView) {
depath++;
}
}
parent = parent.$parent;
}
// 定义存储组件的变量
let componet = null;
// 找到对应组件赋值
const route = this.$router.matched[depath];
if(route) {
componet = route.component;
}
return h(componet)
}
}
export default {
props: {
to: {
type: String | Object,
required: true
}
},
render(h) {
return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
}
}
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
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;
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/vue
if(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值赋值给current
this.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;
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组件,如果是让其层级+1
if(vnodeData.routerView) {
depath++;
}
}
parent = parent.$parent;
}
// 定义存储组件的变量
let componet = null;
// 找到对应组件赋值
const route = this.$router.matched[depath];
if(route) {
componet = route.component;
}
return h(componet)
}
}
export default {
props: {
to: {
type: String | Object,
required: true
}
},
render(h) {
return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
}
}