vue-router的实现原理大概如下:
1.通过用户传入的路由数组生成对应的路由表
2.在install方法中对vue进行全局混入,定义响应式属性_route,方便触发vue的更新
3.通过addEventListener监听hash的改变,或者支持h5的话优先使用pushState(在本文代码中没有使用)
4.实现router-link和router-view组件
代码如下,方便各位理解vue-router这个插件~
let _Vue
let isInstall
export default class VueRouter {
constructor(options) {
// 根实例
this.root = null
// 路由表
this.routes = options.routes
// vue-router记录的当前路由
this.current = getHashPath()
}
// 让vue-router记录下当前的根实例
// 监听url hash改变
init(vm) {
this.root = vm
window.addEventListener('hashchange', () => {
this.current = getHashPath()
// 触发视图更新
this.root._route = this.current
})
}
// this.$router.push
push(path) {
window.location.hash = path
}
static install(Vue) {
// 防止重复实例化
if (isInstall && _Vue === Vue) return
_Vue = Vue
isInstall = true
Vue.mixin({
beforeCreate() {
// 说明是根实例,需要做处理
if (this.$options.router) {
// _routerRoot保存的是根实例,所有的vm属性都会有
this._routerRoot = this
// 这是vue-router实例
this._router = this.$options.router
this._router.init(this)
// 增加响应式
Vue.util.defineReactive(this, '_route', this._router.current)
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
},
})
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
// router-link 组件实现
Vue.component('router-link', {
props: {
to: String
},
render() {
return (
<span onClick={this.handleClick}>{ this.$slots.default }</span>
)
},
methods: {
handleClick() {
window.location.hash = this.to
}
}
})
// router-view组件实现
Vue.component('router-view', {
render(h) {
const routes = this.$router.routes
// 这里有用到我们之前设置的响应式属性
// 当触发Vue更新时
// Vue就会重新渲染这个组件的render函数
let route = this._routerRoot._route
const currentRoute = routes.find(item => item.path === route)
if (!currentRoute) {
return (
<div>未找到</div>
)
}
return h(currentRoute.component)
}
})
}
}
function getHashPath() {
return window.location.hash.slice(1) || '/'
}