首先,我们得弄弄清楚什么是vue-router?vue-router的使用方法和原理是什么?vue-router做了哪些事?最后我们才能参考手写一个router。
Vue Router 是 Vue.js 官⽅的路由管理器。它和 Vue.js 的核⼼深度集成,让构建单⻚⾯应⽤变得易如反掌。
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
...})
import router from './router'
new Vue({
router,
}).$mount("#app");
<router-view></router-view>
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
this.$router.push('/')
this.$router.push('/about')
单⻚⾯应⽤程序中,url发⽣变化时候,不能刷新,显示对应视图内容。
hash #/about
History api /about
router-view
数据响应式:current变量持有url地址,⼀旦变化,动态重新执⾏render。
实现⼀个插件
① 处理路由选项
② 监控url变化,hashchange
③ 响应这个变化
① $router注册
② 两个全局组件:router-link和router-view
import Vue from 'vue'
import VueRouter from './jrouter'
import Home from '../views/Home.vue'
// 1.VueRouter是一个插件?
// 内部做了什么:
// 1)实现并声明两个组件router-view router-link
// 2) install: this.$router.push()
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'),
childen: [
{
path: '/about/info',
component: {
render(h) {
return h('div', 'is a info') } }
}
]
}
]
// 2.创建实例
const router = new VueRouter({
mode: 'hash',
base: process.env.BASE_URL,
routes
})
export default router
// vue插件可以由function构成,必须有install方法,该方法会被vue.use调用
// install方法会会接收一个vue对象参数
// 为了不打包vue,先声明,由install参数赋值
let Vue
class VueRoute {
constructor(options) {
this.$options = options
// window.location.hash性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
// location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,
// 用location.href=url就可以直接将页面重定向url。而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。
this.current = window.location.hash.slice(1) || "/"
// Vue.util.defineReactive可以定义一个响应式对象
Vue.util.defineReactive(this, 'matcher', [])
// 初始化路由数组
this.match()
window.addEventListener('hashchange', () => {
this.current = window.location.hash.slice(1)
// 路由切换时 初始化
this.matcher = []
this.match()
})
}
// 处理matcher数组.
match(routes) {
routes = routes || this.$options.routes
for (const route of routes) {
// 当前地址是否为'/'
if (route.path === '/' && this.current === '/') {
this.matcher.push(route)
return
}
// 当前地址是否不是'/',但包含该路由路径
if (route.path !== '/' && this.current.indexOf(route.path) !== -1) {
this.matcher.push(route)
if (route.childen) {
this.match(route.childen)
}
return
}
}
}
}
VueRoute.install = function (_Vue) {
Vue = _Vue
// 1.挂载$router。
// 由于install会由use调用,调用时,VueRoter对象可能会创建,所以使用全局混入
// VueRouter由根实例(app.vue中的vue对象)传入
// 全局混入: 混入能够增加组件方法的复用性
Vue.mixin({
beforeCreate() {
// 此钩子函数在每个组件创建之前都会执行
// this.$options.router只有根组件存在
if (this.$options.router) {
Vue.prototype.$router = this.$options.router
}
},
})
// 注册实现组件router-view和router-link
Vue.component('router-link', {
props: {
to: {
type: String,
required: true
},
},
render(h) {
return h('a', {
attrs: {
href: '#' + this.to } }, this.$slots.default)
}
})
Vue.component('router-view', {
render(h) {
// 深度标记,作用:标记当前router-view的层次
let depth = 0
// 判断是否是router-veiw
this.$vnode.data.routerVeiw = true
let parent = this.$parent
// 遍历该路由的深度
while (parent) {
const vnodeData = parent.$vnode && parent.$vnode.data
if (vnodeData) {
if (vnodeData.routerVeiw) {
depth++
}
}
parent = parent.$parent
}
let component = null
// find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。
// find() 方法为数组中的每个元素都调用一次函数执行:
// 当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
// 如果没有符合条件的元素返回 undefined
// 注意: find() 对于空数组,函数是不会执行的。
// 注意: find() 并没有改变数组的原始值。
// const route = this.$router.$options.routes.find(
// // 这里不能用this.current,因为this指向当前组件实例
// // 由于Vue.mixin全局混入时已挂载router,故可以取出current
// (route) => route.path === this.$router.current
// )
console.log(this.$router.matcher, depth)
const route = this.$router.matcher[depth]
if (route) {
component = route.component
}
return h(component)
}
})
}
export default VueRoute
这是一篇学习笔记,可能有的地方描述的不是很准确,欢迎大家指出和讨论~~!