popstate
MDN
具体的详解可以看mdn,大致就是当浏览器前进后退的时候会促发popstate事件,通过监听popstate事件来根据地址显示对应的组件。
VueRouter类包含的属性和方法
我们可以通过vue- router的调用来分析他具体包括的属性和方法。
// router.js
import Vue from 'vue'
import VueRouter from '../vueroute/index.js'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
复制代码
// mian.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
复制代码
从以上调用可以分析出VueRouter类需要以下属性和方法
VueRouter
属性 | 类型 | 用途 |
---|---|---|
options | Object | 用于保存初始化VueRouter时传入的options对象 |
data.current | String | 响应式数据,用于存放当前页面的路由地址 |
routeMap | Object | 用于存放传入的路由规则,键名为路由地址,键值为对应的组件 |
方法 | 用途 |
---|---|
install | 静态方法,用于实现vue插件机制,将VueRouter注册到Vue上 |
init | 将各个初始化方法的调用集中在一起 |
initRouteMap | 初始化routeMap对象 |
initComponents | 初始化routerLink和routerView组件 |
initEvents | 初始化监听popstate事件 |
install 方法的实现
let _Vue = null;
export default class VueRouter {
static install (Vue) {
// 1.判断当前插件是否已经被安装
if (VueRouter.install.installed) return
VueRouter.install.installed = true;
// 2.把Vue构造函数记录到全局变量,方便之后方法中使用vue
_Vue = Vue;
// 3.把创建Vue实例时候传入的router对象注入到Vue实例上
// 混入 混入是为了拿到Vue实例,即将this指向未vue的实例
_Vue.mixin({
beforeCreate () {
if (this.$options.router) { // 挂载只需调用一次,所有判断已经挂载后就不再调用
// 将路由对象挂载到vue实例的原型链上
_Vue.prototype.$router = this.$options.router
// 调用路由对象的初始化方法
this.$options.router.init()
}
}
})
}
}
复制代码
constructor的初始化
constructor (options) {
this.options = options // 存入的路由规则
this.routeMap = {} // 路由地址和组件对应关系的对象,键名为路由地址,键值为对应的组件
// 借用vue的observable实现响应式
this.data = _Vue.observable({
current: '/'
})
}
复制代码
initRouteMap 方法的实现
// 初始化routeMap
initRouteMap () {
// 遍历所有的路由规则,把路由规则解析成键值对的形式,存储在routeMap中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
复制代码
initComponents 方法的实现
// 初始化组件
initComponents (Vue) {
const _self = this
// 实现router-link组件
Vue.component('router-link', {
props: {
to: String // 用于接受要跳转的路由地址,简易版,未考虑object形式
},
render (h) {
return h ('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler (e) {
// 改变浏览器地址栏地址
history.pushState({}, '', this.to)
// 更新存储的当前地址
_self.data.current = this.to
// 阻止a标签的默认跳转
e.preventDefault();
}
}
})
// 实现router-view组件
Vue.component('router-view', {
render (h) {
// 根据当前地址找到对应的组件
const component = _self.routeMap[_self.data.current]
// 利用vue的render函数渲染该组件
return h(component)
}
})
}
复制代码
initEvents 方法的实现
// 初始化事件
initEvents () {
// 创建监听事件
window.addEventListener('popstate', (event) => {
// 更新当前路由地址
this.data.current = window.location.pathname
});
}
复制代码
init 方法的实现
init () {
// 初始化routeMap对象
this.initRouteMap()
// 创建router-link和router-view组件
this.initComponents(_Vue)
// 初始化监听popstate事件
this.initEvents()
}
复制代码
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 = Vue;
// 3.把创建Vue实例时候传入的router对象注入到Vue实例上
// 混入 混入是为了拿到Vue实例,即将this指向未vue的实例
_Vue.mixin({
beforeCreate () {
if (this.$options.router) { // 挂载只需调用一次,所有判断已经挂载后就不再调用
// 将路由对象挂载到vue实例的原型链上
_Vue.prototype.$router = this.$options.router
// 调用路由对象的初始化方法
this.$options.router.init()
}
}
})
}
constructor (options) {
this.options = options // 存入的路由规则
this.routeMap = {} // 路由地址和组件对应关系的对象,键名为路由地址,键值为对应的组件
// 借用vue的observable实现响应式
this.data = _Vue.observable({
current: '/'
})
}
init () {
// 初始化routeMap对象
this.initRouteMap()
// 创建router-link和router-view组件
this.initComponents(_Vue)
// 初始化监听popstate事件
this.initEvents()
}
// 初始化routeMap
initRouteMap () {
// 遍历所有的路由规则,把路由规则解析成键值对的形式,存储在routeMap中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
// 初始化组件
initComponents (Vue) {
const _self = this
// 实现router-link组件
Vue.component('router-link', {
props: {
to: String // 用于接受要跳转的路由地址,简易版,未考虑object形式
},
render (h) {
return h ('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler (e) {
// 改变浏览器地址栏地址
history.pushState({}, '', this.to)
// 更新存储的当前地址
_self.data.current = this.to
// 阻止a标签的默认跳转
e.preventDefault();
}
}
})
// 实现router-view组件
Vue.component('router-view', {
render (h) {
// 根据当前地址找到对应的组件
const component = _self.routeMap[_self.data.current]
// 利用vue的render函数渲染该组件
return h(component)
}
})
}
// 初始化事件
initEvents () {
// 创建监听事件
window.addEventListener('popstate', (event) => {
// 更新当前路由地址
this.data.current = window.location.pathname
});
}
}
复制代码
总结
旨在分析原理,所以只实现了最简版本的VueRouter,routerLink组件的to属性只支持了String类型的传参,大家可以稍加修改适配对象类型的传参
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END