vue-router
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')
- 步骤四:添加路由视图,APP.vue
<router-view></router-view>
- 导航
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
vue-router源码实现
需求分析:
- 作为一个插件存在:实现VueRouter类和install方法
- 实现两个全局组件:router-view用于显示匹配组件内容,router-link用于跳转
- 监控url变化:监听hashchange或popstate事件
- 响应最新url:创建一个响应式的属性current,当它改变时获取对应组件并显示
实现一个插件:创建VueRouter类和install方法
创建kue-router.js
let Vue; // 引用构造函数,VueRouter中要使用
// 保存选项
class VueRouter{
constructor(options) {
this.$options = options
}
}
// 插件:实现install方法,注册$router
VueRouter.install = function(_Vue) {
// 引用构造函数,VueRouter中要使用
Vue = _Vue
Vue.mixin({
beforeCreate() {
// 只有根组件拥有router选项
if(this.$options.router) {
// vm.$router
Vue.prototype.$router = this.$options.router
}
}
})
}
export default VueRouter
注:为什么要用混入方式写?主要原因是use代码在前,Router实例创建在后,而install逻辑又需要用到该实例
创建router-view和router-link
创建krouter-link.js
export default {
props: {
to: String,
required: true
},
render(h) {
// return <a href={'#' + this.to}>{this.$slots.default}</a>
return h('a', {
attrs: {
href: '#' + this.to
}
}, [
this.$slots.default
])
}
}
创建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)
}
}
动态获取对应组件,krouter-view.js
export dfault {
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].component
return h(component)
}
}