系列文章:
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,下一期不见不散