Vue Router是Vue.js官方的路由管理器。它是Vue.js的核心深度集成,让构建单页面应用变得易如反掌。
使用
安装:vue add router
核心步骤:
- 步骤一:使用vue-router插件,router.js
import Router from 'vue-router'
vue.use(Router)
- 步骤二:创建Router实例,router.js
export default new Router({...})
- 步骤三:在根组件上添加该实例,main.js
import router from './router'
new Vue({
router,
}).$mount("#app");
- 步骤四:添加路由视图
<router-view></router-view>
- 导航
<router-link></router-link>
源码实现
** 作为一个插件存在:实现VueRouter类和install方法 **
** 实现两个全局组件:router-view用于显示匹配组件内容,router-link用于跳转 **
** 监控url变化:监听hashchange或popstate事件 **
** 响应最新url:创建一个响应式的属性current,当它改变时获取对应组件并展示 **
实现一个插件:创建VueRouter类和install方法
创建my-router.js
let vue // 引用构造函数,VueRouter中要使用
class VueRouter {
constructor(options) {
this.options = options
}
}
//插件:实现install方法,注册$router
VueRouter.install = function(_Vue) {
//引用构造函数,VueRouter中要使用
Vue = _Vue;
// 任务1:挂载$router
Vuemixin({
beforeCreate() {
//只有根组件拥有router选项
if(this.$options.router) {
// vm.$router
Vue.prototype.$router = this.$options.router
}
}
});
//任务2: 实现两个全局组件router-link和router-view
Vue.component('router-link',Link);
Vue.component('router-view',View)
}
export default VueRouter;
为什么要混入方式写?主要原因是use代码在前,Router实例创建在后,而install逻辑又需要用到该实例
创建router-view和router-link
创建my-link.js
export default {
props: {
to: String,
required: true
},
render(h) {
return h('a', {
attrs: {
href: '#' _ this.to
}
},[
this.$slots.default
])
}
}
创建my-router-view.js
export default {
render (h) {
//暂时不渲染任何东西
return h(null);
}
}
监听url变化
定义响应式的current属性,监听hashchange事件
class VueRouter {
constructor(options) {
// current应该是响应的
Vue.util.defineReactive(this, 'current', '/')
//定义响应式的属性current
const initial = window.location.hash.slice(1) || '/'
Vue.util.defineReactive(this,'current',initial)
//监听hashchange事件
window.addEventListener('hashchange',this.onHashChange.bind(this))
window.addEventListener('load', this.onHashChange.bind(this))
}
onHashChange() {
this.current = window.location.hash.slice(1)
}
}
动态获取对应组件,my-router-view.js
export default {
render(h) {
// 动态获取对应组件
let component = null;
this.$router.$options.routes.forEach(route=> {
if(route.path === this.$router.current) {
component = route.component
}
});
return h(component)
}
}
提前处理路由表
提前处理路由表避免每次都循环
class VueRouter {
constructor(options) {
// 缓存path和route映射关系
this.routeMap = {}
this.$options.routes.forEach(route => {
this.routeMap[route.path] = route
})
}
}
使用,krouter-view.js
export default {
render(h) {
const { routeMap, current } = this.$router
const component = routeMap[current] ? routeMap[current].component : null
return h(component)
}
}