Vue源码学习之VueRouter
文章内容输出来源:拉勾教育大前端高薪训练营
在拉钩训练营学习已经有一段时间了,感觉这段时间的收获远比自己独自学习强的多,自己学习的时候经常会因为惰性,无法坚持,在这里有班主任时刻关注你的学习进度(感觉对我这种懒人蛮好的),最重要的是有了一个学习的计划,不用无头苍蝇一样东一点西一点,最后什么都没记住。学习都是需要方法和技巧的,在训练营会有专业的老师为你答疑解惑,同时会教你解决问题的思路及方法,让你能够触类旁通。这篇文章主要是Vue源码中VueRouter的模拟实现:
最近在学习使用VueRouter的源码,这里简单介绍下VueRouter的实现
什么是Vue.use
- 我们在引入一些插件的时候经常需要在main里面用到这个语法
- Vue.use(function/object),内部如果是函数直接调用,是对象则调用对象的install方法
VueRouter源码
- VueRouter 是使用Vue.use引用的,所以VueRouter必定会有一个install方法
- VueRouter分为history, hash,hash模式 基于锚点及onhashchange事件,history模式 基于html5中的historyAPI(pushstate,replacestate)以及popstate
下面是具体代码:
main.js 入口文件
import Vue from 'vue'
import App from './App.vue'
import router from './router' //router中导出的VueRouter实例
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
上面是Vue项目的入口文件,从router中引入的VueRouter的实例,然后作为参数实例化一个Vue实例
Router.js
import Vue from 'vue'
// import VueRouter from 'vue-router'
import VueRouter from '../sourceCode/router' //VueRouter实现的源码
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'hash',
base: process.env.BASE_URL,
routes
})
export default router
上面是vueRouter的具体使用,我们在这获取VueRouter的相关参数
源码具体实现(我们在使用VueRouter时先使用Vue.use引入,执行其中的intsall,然后在导出一个实例化的VueRouter对象)
步骤详解
install方法
- 1.判断当前插件是否已安装
- 2.将Vue构造函数记录到全局变量
- 3.创建Vue实例时传入的router对象注入到所有vue实例,同时初始化 routeMap, component, event,mode等参数
initRouteMap
- 遍历所有的路由规则,将路由规则解析成键值对 存储到routeMap
initComponents
- 生成全局组件router-link, router-view
initEvent
- history模式:当活动历史记录条目更改时,将触发popstate事件。如果被激活的历史记录条目是通过
history.pushState的调用创建的,或者受到对history.replaceState()的调用的影响,popstate事件的
state属性包含历史条的状态对象的副本。需要注意的是调用history.pushState()或history.replaceState
不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮
(或者在Javascript代码中调用history.back()或者history.forward()方法) - hash模式:当活动历史记录条目更改时,将触发hashchange事件。
…/sourceCode/router.js
let _Vue = null
export default class VueRouter {
constructor(options) {
this.options = options
this.routeMap = {} //路由地址组件键值对
this.mode = options.mode || 'hash'
this.data = _Vue.observable({
current:'/'
}) //创建响应式对象每当地址变化因为是响应式对象,触发router-view组件的更新
}
//Vue的构造函数 args可选参数
static install (Vue) {
//1.判断当前插件是否已安装
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
//2.将Vue构造函数记录到全局变量
_Vue = Vue
//3.创建Vue实例时传入的router对象注入到所有vue实例
//混入
_Vue.mixin({ //在每个vue实例内部
//this指向vue的实例对象
beforeCreate() {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init() //对应VueRouter实例对象中的init
}
},
})
}
initRouteMap() {
//遍历所有的路由规则,将路由规则解析成键值对 存储到routeMap
this.options.routes.map(route => {
this.routeMap[route.path] = route.component
})
}
initComponents(Vue) {//定义全局组件
let that = this
Vue.component('router-link', {
props:{
to:String
},
// this指向的是当前组件vue实例
render(h) {
let props = {
attrs:{
href:this.$router.mode === "hash" ? "/#" + this.to : this.to//hash模式拼接#号
},
};
if (this.$router.mode === "history") {
props.on = {
click:this.clickHandle
}
}
return h('a', props, [this.$slots.default])
},
methods: {
clickHandle(e) {
e.preventDefault();
history.pushState('', '', this.to)
this.$router.data.current = this.to // $router=>VueRouter实例
}
},
})
Vue.component('router-view', {
render(h) {
const com = that.routeMap[that.data.current]
return h(com)
}
})
}
init() {
this.initRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
initEvent () {//定义事件
if (this.mode === "history") {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
} else if (this.mode === 'hash') {
window.addEventListener('hashchange', () => {
console.log('hashchange')
this.data.current = window.location.hash.substr(1)
})
}
}
}