系列文章:
Vue是怎么注册插件的呢?
使用过Vue的coder都知道,如果想注册一个vue的插件,在vue对象上能够使用的话(并不是绑在Vue.prototype上的那种暴力方式),必须使用Vue.use(你的插件)的方式来注册插件,
用use注册插件需要注意什么呢?
Vue.use方法会寻找插件上的install方法,并且执行,如果插件没有install方法的话,就会报错,无法使用use来注册插件。 哦哟,是真的傲娇的一匹,所以想要使用VueRouter插件的话,就必须拥有一个install方法,
这么一看Vue的插件都需要install方法
所以我们来看看VueRouter的install方法都做了什么?
代码实在是不少,我是真的没办法一口气复制过来,截个图先给大家做一个展示吧install 做了那几件事?
简单看来: Vue.mixin:侵入式的对每个Vue component进行了扩展
用ES5的defineProperty的方式设置$router
与 $route
属性。
创建了routerView 与RouterLink两个组件
对路由钩子进行一个统一设置(这块没看Vue.config.optionMergeStrategies)的源码,目前还不是很熟,当然这不重要了。先往下看
Vue-Router如何使用Vue的内容
大家都知道,Vue.mixin、Vue.component都是Vue类的方法,Vue-Router想要使用的话就必须引入Vue,这是毋庸置疑的,但是如果把Vue打包进入Vue-router的源码内,这必然导致Vue-router的包变得很大,
关键问题!!! 目前没有发现用Vue开发时哪个使用Vue-router开发不提前引入Vue的。
// 声明一个私有的_Vue用来接收外部的Vue类。
export let _Vue
//install方法接收一个参数,也就是Vue类
export function install (Vue) {
// 如果已经被注册了,并且
if (install.installed && _Vue === Vue) return
install.installed = true
//把Vue类赋值给私有_Vue
_Vue = Vue
这种方式只需要在install的时候使用全局的Vue类,并不需要将Vue打包进入Vue-router的源码内。
减少不必要的依赖,从我大Vue-router做起。
const isDef = v => v !== undefined
//这是install方法中一个判断值是否为undefined的方法,在接下来会有很多次调用到它,请不要遗漏
// 注册实例的方法
const registerInstance = (vm, callVal) => {
//vm是什么?就是vue componet实例
let i = vm.$options._parentVnode
// 这是一个类似链式调用的方式
// 目的是确保能确定到this.$options._parentVnode.data.registerRouteInstance是不是存在?
// 如果找到了那么就自然而然的把i赋值为这个方法,然后执行它
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
说到这里可能会很乱,我们先留下来这个内容,一会去看看哪里会调用它。
mixin 侵入了哪里?
Vue.mixin({
beforeCreate () {
// 只有根节点的$options的属性中有router
if (isDef(this.$options.router)) {
this._routerRoot = this
this._router = this.$options.router
this._router.init(this)
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
},
destroyed () {
registerInstance(this)
}
})
看到这里我们应该了解,Vue-Router在install的时候侵入了每个vue componet的beforeCreate以及destroyed生命周期钩子中,在创建之前以及摧毁的时候做一些事情。 做了什么呢? 我们好像看到了registerInstance方法。
// 接受了vue componet自己
registerInstance(this, this)
这个时候重新看一下这个方法,意思就是寻找这个方法去调用一下,this.$options._parentVnode.data.registerRouteInstance(这个方法谁有呢?) 这个我也不知道,我就写在一个子组件内的方法,去查看一下它的父组件们族谱中谁有这个方法。
(function(t){
while(t){
if(typeof t.$options._parentVnode.data.registerRouteInstance === 'function'){
return t
}else{
t = t.$parent
}
}
return 'not find'
})(t)
各位看官这是一个很拙劣的方法(勿学)以后会优化这个方法的。 然后发现这个this是谁呢?就是在你使用vue-router时,那个我们对每个组件设置path的那个组件。可以看下面
new vueRouter({
xxx
routes : [{
path : 'xxx',
component : '某某组件'
}]
})
每个有这个方法的应该都是这里面设置过的组件,所有我就有一个猜测,是不是路由的改变会导致该路径跳转前后的组件进行生成与摧毁(展示与隐藏)在这些组件创建之前会调用registerInstance方法(这个方法我还没看内部实现)。以后会去验证这个猜测的准确程度。
终于知道这个registerInstance什么时候执行了
router这个属性谁有啊????
//this.$options.router是绑定在app根节点的组件上。所有只有根组件有这个router属性
//所以所有的vue component的_routerRoot、_router、都是一样的。
// this.$options.router存在吗?==》 是不是已经绑定万_routerRoot的根节点
if (isDef(this.$options.router)) {
// 根节点的_routerRoot就是根节点的vue component
this._routerRoot = this
// vue router实例
this._router = this.$options.router
// 执行init
this._router.init(this)
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
// 如果没有这个属性 ==》 两种情况 还没绑定这个属性的根节点、不是根节点
// 未绑定属性的根节点组件,(根节点组件怎么会有爸爸呢)它不存在$parent属性、所以还是指向了自己
// 不是根节点组件,那就找它爸爸的_routerRoot属性,用它爸爸的
// vue的子组件beforeCreate肯定晚于父组件beforeCreate所以
// 所有的组件就像一棵组件树以一样大家,从根向所有树枝树杈去传递这个属性
// 大家都是用一个属性所以每个组件的_routerRoot都是根节点组件
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
所以大家可以尝试的在vue项目中打印出组件本身,查看_routerRoot属性时,每个组件的uid都是一样的,$el也是根节点标签的id 。这不实锤了!!
总结一下
我们可以看出来在vueRouter在注册方面有哪些重要的事?
-
1:并没有将Vue打包进入Vue-router插件,而是使用外部Vue引入的方式。
-
2: mixin侵入式的给每个Vue组件的beforeCreate与destroyed生命周期对组件指定路由组件进行了创建与摧毁。
-
3:所有Vue组件的_router都是同一个Vue-router实例,总之大家都是用一个的。
install方法就介绍到这里,下一节会开始讲述Vue-router的主构造函数。前端er,下一期不见不散