核心:
- 实现前端路由(通过H5的hiostory.pushState API实现)
- 注册为vue插件
- 实现原vuerouter插件的router-link和router-view组件
let _Vue = null;
export default class VueRouter {
// Vue通过Vue.use方法注册插件
// 该方法支持接收一个参数,类型为函数或者对象
// 函数则在实例化时直接调用
// 对象则在实例化时调用其install方法,调用时会将Vue实例作为参数传入
/**
*
* @param {import('vue').ComponentOptions} Vue
*
*/
static install (Vue) {
// 确认是否安装过该插件
if(this.install.installed) return;
this.install.installed = true;
// 将传入的Vue实例转为全局变量,方便后面调用其方法
_Vue = Vue;
// 将Vue实例化时传入的router对象绑定到Vue原型的$router上,使Vue的所有组件(Vue实例)都能调用
// 使用vue混入方法
_Vue.mixin({
beforeCreate() {
if(this.$options.router) {
// this.$options.router是初始化Vue实例时传入的router参数,也就是VueRouter实例
_Vue.prototype.$router = this.$options.router;
// 调用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提供的响应式对象方法创建响应式对象data用于监听并存储当前路由
this.data = _Vue.observable({
current: '/'
})
}
init() {
this.initRouteMap()
this.initComponent()
this.initEvent()
}
// 用于注册和实现router-link和router-view组件
initComponent() {
_Vue.component('router-link', {
props:{
to: String
},
// 使用vue-cli初始化的vue项目默认使用运行时vue,不支持直接使用template,因此使用render函数渲染组件
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实例内部调用方法this指向Vue实例,故提前进行存储
const self = this;
_Vue.component('router-view', {
render(h) {
// 根据当前data.current的值匹配当前渲染的组件
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
// 用于监听浏览器的popstate事件动态渲染当前路由对应组件
initEvent() {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname;
})
}
// 根据当前路由将传入的options解析为{路径:组件}这种键值对的格式来渲染当前路由组件
initRouteMap() {
this.options.routes.forEach( route => {
this.routeMap[route.path] = route.component
})
}
}