乐趣区

深入理解Vue的插件机制与install

前言

我们在使用 Vue 的时候,经常会使用并写一些自定义的插件,然后利用 Vue.use 引入。所以提到写插件,install 这个方法是必不可少的。Vue.js 的插件应该暴露一个 `install` 方法。这个方法的第一个参数是 `Vue` 构造器,第二个参数是一个可选的选项对象 。这是 Vue 官方对 Vue 插件的规范。那这 install 函数到底是什么东东呢,Vue 内部到底用它做了什么处理,怎么调用的,今天我就给大家伙从源码层面把他整的明明白白。
看完这篇文章,你将学到:

  • install 函数可以做些什么;
  • install 内部是怎么实现的;
  • Vuex,Vue-Router 插件在 install 期间到底干了什么;

好啦,闲话不多说,咱们直接开始!!!

install 在 Vuex&Vue-Router 中的处理

这里先抛出两个问题,大家可以思考下,算是挖坑,下面再逐一解答:

  • 为什么我们在项目中可以直接使用 $router $store 来获取其中的值以及一些方法;
  • 为什么使用这俩插件都是先用 Vue.use 引入。然后才创建实例,在 Vue 实例中传入;

二者其实原理相同,这里我们用 Vue-Router 来举例,首先我们来看一下它内部 install 的具体实现:

class Router {constructor(options) {...}
}

Router.install = function(_Vue) {

    _Vue.mixin({beforeCreate() {if (this.$options.router) {_Vue.prototype.$router = this.$options.router}
        }
    })

}

export default Router;
  • _Vue.mixin 全局混入是什么呢?相当于在所有的组件中混入这个方法;
  • beforeCreate 是什么呢?当然是 Vue 的一个生命周期,在 create 之前执行;

既然如此,我们大胆的做一个判断。Vue-Router 其实是在 install 函数里面使用了一个全局混入,在 beforeCreate 这个生命周期触发的时候把 this.$options.router 挂载到 Vue 的原型上,这样我们就可以使用 this.$router 来调用 router 实例啦。
同学 A:等一下,stop!!!你说的我很李姐,但是 this.$options.router 这又是什么东西,从哪来的啊?

安啦,这咱们才刚刚解决了第一个问题,下面咱们来填第二个坑。

咱们平时使用 Vue-Router,以及定义入口文件的 Vue 实例大概是这样子

// router/index.js
import VueRouter form 'vue-router';
import Vue from 'vue';

Vue.use(VueRouter);

const _router = [...]

const Router = new VueRouter(_router);

export default Router;

// main.js
import Vue from 'vue';
import router from 'router';

new Vue({
    router,
    ...
}).$mount('#app');

结合最开始的例子,我们先来分析一波。

  • Vue.use()主要是调用插件内部的 install 方法,并将 Vue 实例作为参数传入;
  • 上面使用的是this.$options.router,options 通常代表的是配置项;
  • 在 main.js 中我们把 Router 实例作为配置项传入到 Vue 实例中

叮!!!要素察觉,那我们来大胆推测一波。
Vue-Routeruse 其实是做了一个全局混入,为了在合适的时间点,获取到 Vue 根实例配置项中的 router 实例,执行挂载。紧接着在 new Vue() 根实例创建的时候,注入 router 实例,然后触发全局混入中的生命周期,这个时候根实例的配置项 this.$options 已经包含了 router 实例,最后完成挂载流程!!!
光这一段的代码也是逻辑缜密,编程思路巧妙,令人直呼内行啊!兄弟萌,把内行打在公屏上,hhhh。

install 在 Vue 中的内部实现

看完了常用库 install 的使用,不知大家是否有收获。接下来热身结束后,我们就可以开始看一看 install 内部实现了,先上源码。

export function initUse (Vue: GlobalAPI) {
    // 注册一个挂载在实例上的 use 方法
    Vue.use = function (plugin: Function | Object) {
        // 初始化当前插件的数组
        const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
        // 如果这个插件已经被注册过了,那就不作处理
        if (installedPlugins.indexOf(plugin) > -1) {return this}

        ...
        
        // 重点来了哦!!!if (typeof plugin.install === 'function') {
        // 当插件中 install 是一个函数的时候,调用 install 方法,指向插件,并把一众参数传入
            plugin.install.apply(plugin, args)

        } else if (typeof plugin === 'function') {
        // 当插件本身就是一个函数的时候,把它当做 install 方法,指向插件,并把一众参数传入
            plugin.apply(null, args)

        }
        
        // 将插件放入插件数组中
        installedPlugins.push(plugin)

        return this
    }
}

源码这部分写的很简洁,可读性很高。就是在 use 的时候,判断插件类型,执行 install 或者插件本身。其实细化一下官网的解释就是,Class 类的插件应该暴露一个 install 方法。

结语

这次的分享到这里就结束啦,不知道大家对于 Vue 的插件机制有没有更深入的了解呢?其实开发插件的时候利用 install 我们可以做茫茫多的事儿。
比如 Vue-Router 在 install 中其实还注册了 Router-viewRouter-link的全局组件。感兴趣的同学们可以去看一下 vue-router 的原理?我们来手撸一个 vue-router!
再次感谢你的阅读。好啦,兄弟萌再见咯!

退出移动版