大家好,我是草舟,跟我一起学前端👋。
今天,我们从基本使用开始,理解并实现一个vue-router
1.1 Vue Router使用步骤
router/index.js
// 【第一步】导入vue、vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'
// 【第二步】注册路由插件
Vue.use(VueRouter) // use方法如果传入函数-直接调用函数,如果传入对象-调用对象的install方法
// 路由规则
const routes = [
{
path: '/',
name: 'Index',
component: () => import(/* webpackChunkName: "xxx" */ '../views/xxx.vue')
}
...
]
// 【第三步】创建router对象
const router = new VueRouter({
mode: '', // 默认hash , 可选history
base: xxx, // 基地址
routes
})
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
// 【第四步】 注册router对象(给Vue实例注入$route和$router)
router,
render: h => h(App)
}).$mount('#app')
App.vue
// 【第六步】创建链接
<router-link to="/"> Index </router-link>
// 【第五步】创建路由组件的占位
<router-view />
1.2 Hash模式和History模式的区别
表现形式的区别:
- Hash模式:caozhou.666.com/#/home?id=1…
- History模式:caozhou.666.com/home/123456
原理上的区别:
- Hash 模式是基于锚点,以及 onhashchange 事件
- History 模式是基于HTML5的History API
- history.pushState() IE10后才支持
- history.replaceState()
1.3 VueRouter-分析
- 【第一步】实现router/index.js中引入的vue-router文件
- 【第二步】实现注册路由插件调用的install方法
- 【第三步】利用混入,把创建Vue实例时候传入的router对象注入到Vue实例上
- 【第四步】编写createRouteMap方法,遍历所有的路由规则,解析成键值对的形式,存储到routeMap中
- 【第五步】编写initComponents方法来创建router-link和router-view组件
- 【第六步】编写initEvent方法监听路由返回触发的history的popstate方法改变当前路由地址
1.4 VueRouter-实现
let _Vue = null
export default class VueRouter {
static install (Vue) {
//1. 判断当前插件是否已经被安装
if(VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
//2. 把Vue构造函数记录到全局变量
_Vue = Vue
//3. 把创建Vue实例时候传入的router对象注入到Vue实例上
// _Vue.prototype.$router = this.$options.router
// 混入
_Vue.mixin({
beforeCreate() {
if(this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
// 构造函数
constructor(options) {
this.options = options
this.routeMap = {}
this.data = _Vue.observable({
current: '/' // 当前路由默认地址
})
}
init() {
this.createRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
// createRouteMap
createRouteMap() {
// 遍历所有的路由规则,解析成键值对的形式,存储到routeMap中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
// initComponents
initComponents(Vue) {
Vue.component('router-link', {
props: {
to: String
},
render(h) {
return h('a', {
attrs: {
href: this.to
},
on: {
click:this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler (e) {
// 调用pushState(data, title, url)方法改变浏览器地址栏,但不会向服务器发送请求
history.pushState({}, '', this.to)
this.$router.data.current = this.to
e.preventDefault()
}
}
})
const self = this
Vue.component('router-view', {
render(h) {
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
initEvent() {
Window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
}
}