1.分析
Vue Router 有两种常用的模式:hash模式和history模式。这里首先尝试下用hash模式封装一个简单版的Vue Router。
- hash模式: 使用url#后面的锚点来区分组件,hash改变的时候,页面不会重新加载,只会触发onhashchange事件
- history模式: 这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈, 在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。
2.实现
let Vue
class Router {
static install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if(this.$options.router) {
Vue.prototype.$flyRouter = this.$options.router
this.$options.router.init()
}
}
})
}
constructor(options) {
this.$options = options
this.routeMaps = {}
this.app = new Vue({
data: {
currentUrl: '/'
}
})
}
init() {
this.bindEvents()
this.generateHashMap()
this.createComponent(Vue)
}
bindEvents() {
window.addEventListener('hashchange', this.hashChangeEvent.bind(this), false)
window.addEventListener('load', this.hashChangeEvent.bind(this), false)
}
hashChangeEvent(e) {
let hash = this.getHash()
let { from, to } = this.getFrom(e)
let router = this.routeMaps[hash]
if(router.beforeEnter) {
router.beforeEnter(to, from, () => {
this.app.currentUrl = hash
})
}else {
this.app.currentUrl = hash
}
}
getFrom(e) {
let from = e.oldURL || ''
let to = e.newURL || '/'
return {
from,
to
}
}
getHash() {
return window.location.hash.substr(1) || '/'
}
push(url) {
window.location.hash = url
}
generateHashMap() {
this.$options.routes.forEach(item => {
this.routeMaps[item.path] = item.component
})
}
createComponent(Vue) {
const that = this
Vue.component('router-link',{
render(h) {
return h('a', {
attrs: {
href: '#' + this.to
}
},
[this.$slots.default]
)
},
props: {
to: String
}
})
Vue.component('router-view', {
render(h) {
let component = that.routeMaps[that.app.currentUrl]
return h(component)
}
})
}
}
export default Router
3.应用
在router/index.js里引用自定义的router.js
import Vue from 'vue'
import Router from '@/utils/router'
import Home from '@/pages/Home.vue'
import About from '@/pages/About.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: Home
},
{
path: '/about',
component: About,
beforeEnter: (to,from,next) => {
console.log('输出',to,from)
setTimeout(() => {
next()
}, 3000)
}
}
]
})
App.vue里的代码
<template>
<div id="app">
<router-link to="/about">去about页</router-link>
<router-link to="/">来home页</router-link>
<router-view></router-view>
</div>
</template>
main.js里的代码
import Vue from 'vue'
import App from './App'
import router from '@/router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
4.总结
从以上代码可以看出,自定义的router已经基本实现了Vue Router的功能,其用法和Vue Router一致。