核心:
- 實現(xiàn)前端路由(通過H5的hiostory.pushState API實現(xiàn))
- 注冊為vue插件
- 實現(xiàn)原vuerouter插件的router-link和router-view組件
let _Vue = null;
export default class VueRouter {
// Vue通過Vue.use方法注冊插件
// 該方法支持接收一個參數(shù)瞒渠,類型為函數(shù)或者對象
// 函數(shù)則在實例化時直接調(diào)用
// 對象則在實例化時調(diào)用其install方法籽暇,調(diào)用時會將Vue實例作為參數(shù)傳入
/**
*
* @param {import('vue').ComponentOptions} Vue
*
*/
static install (Vue) {
// 確認是否安裝過該插件
if(this.install.installed) return;
this.install.installed = true;
// 將傳入的Vue實例轉(zhuǎn)為全局變量哩照,方便后面調(diào)用其方法
_Vue = Vue;
// 將Vue實例化時傳入的router對象綁定到Vue原型的$router上,使Vue的所有組件(Vue實例)都能調(diào)用
// 使用vue混入方法
_Vue.mixin({
beforeCreate() {
if(this.$options.router) {
// this.$options.router是初始化Vue實例時傳入的router參數(shù)杏愤,也就是VueRouter實例
_Vue.prototype.$router = this.$options.router;
// 調(diào)用vuerouter對象初始化方法
this.$router.init()
}
}
})
}
/**
*
* @param {Object} options
* @property {Object} options
* @property {Array} options.routes
* @property {Object} routeMap
*/
constructor(options) {
// 接收實例化時傳入的routes配置
this.options = options;
// 通過方法將傳入options解析為{路徑:組件}這種鍵值對的格式來渲染當前路由組件
this.routeMap = {}
// 使用Vue提供的響應式對象方法創(chuàng)建響應式對象data用于監(jiān)聽并存儲當前路由
this.data = _Vue.observable({
current: '/'
})
}
init() {
this.initRouteMap()
this.initComponent()
this.initEvent()
}
// 用于注冊和實現(xiàn)router-link和router-view組件
initComponent() {
_Vue.component('router-link', {
props:{
to: String
},
// 使用vue-cli初始化的vue項目默認使用運行時vue鸦列,不支持直接使用template拆魏,因此使用render函數(shù)渲染組件
render(h) {
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.handleClick
}
}, [this.$slots.default])
},
methods: {
/**
* @param {Event} e
*/
handleClick(e) {
// HTML5 新api气筋,在不刷新頁面的前提下更改url
window.history.pushState({}, '', this.to);
// 同時修改data.current的值缝驳,匹配當前組件
this.$router.data.current = this.to;
// 阻止a標簽刷新頁面
e.preventDefault()
}
}
})
// Vue實例內(nèi)部調(diào)用方法this指向Vue實例连锯,故提前進行存儲
const self = this;
_Vue.component('router-view', {
render(h) {
// 根據(jù)當前data.current的值匹配當前渲染的組件
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
// 用于監(jiān)聽瀏覽器的popstate事件動態(tài)渲染當前路由對應組件
initEvent() {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname;
})
}
// 根據(jù)當前路由將傳入的options解析為{路徑:組件}這種鍵值對的格式來渲染當前路由組件
initRouteMap() {
this.options.routes.forEach( route => {
this.routeMap[route.path] = route.component
})
}
}