1.入口
一般情况下,在一个项目中入口文件为index.js。
VueRouter的源码index.js中有一小部分是关于install方法的,也是我们分析install方法的入口。
import { install } from './install'
// 挂载install;
VueRouter.install = install
VueRouter.version = '__VERSION__'
// 判断如果window上挂载了Vue则自动使用插件;
if (inBrowser && window.Vue) {
window.Vue.use(VueRouter)
}
在这里,可以发现VueRouter的实例以类(对象)方法的形式设置了install属性和version属性。
同时设置了当为浏览器环境且window中包含Vue实例时调用use方法安装VueRouter,需要了解的则是在use方法中调用了VueRouter的install方法
2.方法主体
1. _Vue的写法
在install方法的主体中,首当其冲的是一段对_Vue的处理。
// export一个Vue的原因是可以不将Vue打包进插件中而使用Vue一些方法;
// 只能在install之后才会存在这个Vue的实例;
export let _Vue
export function install (Vue) {
// 如果已经执行了install方法,则不再重复执行
if (install.installed && _Vue === Vue) return
install.installed = true
// 把 Vue 赋值给全局变量
_Vue = Vue
...
Vue.xxx
}
因为install方法在执行的时候,传入的参数就是在调用use方法的Vue实例,所以这里可以使用一个全局变量类型的_Vue来接收Vue,使得在VueRouter中的其他地方也可以调用到全局变量_Vue从而可以使用一些Vue中的方法。
同时,这也使得Vue可以不被引入,减少了代码的打包体积。
实际上,这种写法在Vue的插件中很常用,Vuex中也是这种形式来接收Vue
2. mixin混入
mixin混入功能,在我看来就是使用了类似于obj.prototype.xxx形式添加了新的功能方法,从而使得该功能可以被子类调用。
// 采用混入方式,在beforeCreate和destoryed方法中处理路由相关操作
Vue.mixin({
beforeCreate () {
// 如果已经挂载了router属性,
if (isDef(this.$options.router)) {
// 保存vue实例到this._routerRoot
this._routerRoot = this
// 保存vue实例数据中的router,即为挂载的route实例
this._router = this.$options.router
// router实例进行初始化
this._router.init(this)
// 添加属性_route,实现双向绑定,触发组件渲染,存放当前页面路径
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
// 将每一个组件的_routerRoot都指向根Vue实例;
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
// 注册VueComponent 进行Observer处理;
registerInstance(this, this)
},
destroyed () {
// 注销VueComponent
registerInstance(this)
}
})
- Vue实例包括根实例和子实例,根实例可直接进操作,而子实例则需要不断寻找它的父实例,从而使得每一个子实例的_routerRoot都指向Vue根实例。
- 这里面有几个变量是需要注意的:
- $options下的router即为实例化Vue的时候挂载vue-router实例;
- _routerRoot指向我们的Vue根节点;
- _router存储的就是我们从$options中拿到的vue-router对象;
- _route是一个响应式的路由route对象,存储路由信息。它是通过Vue提供的Vue.util.defineReactive来实现响应式的。
3. 定义两个全局组件
import View from './components/view'
import Link from './components/link'
// 注册全局组件 router-view router-link
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
这两个就是可以全局使用的router-link和router-view组件。
两个组件都是函数类组件,具体分析留待后续,暂不展开。
4. 设置两个原型对象
// 给vue实例的原型添加$router属性,
// 使得可以通过this.$router方式调用router相关方法
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
// 给vue实例的原型添加$route属性,
// 使得可以通过this.$route方式调用route相关方法
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
需要了解的东西:
- this. r o u t e r 指 向 的 是 t h i s . r o u t e r R o o t . r o u t e r , 即 为 从 router指向的是 this._routerRoot._router ,即为从 router指向的是this.routerRoot.router,即为从options中拿到的VueRouter对象,所以可以使用push、back、go等方法。
- this.$route指向的是this._routerRoot._route,即为存储路由信息的响应式对象,所以可以从中获取到query、path、params等属性。