vue.use源码分析

Posted 昨日前端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue.use源码分析相关的知识,希望对你有一定的参考价值。

Vue.ues()方法从源码分析

最近再重看vue官方文档时,看到下面这一块,很是好奇为什么vue.ues()就可以使用自己开发的插件了,于是研究了一下源码。

开发插件

Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:

MyPlugin.install = function (Vue, options) {
 // 1. 添加全局方法或 property
 Vue.myGlobalMethod = function () {
   // 逻辑...
}

 // 2. 添加全局资源
 Vue.directive('my-directive', {
   bind (el, binding, vnode, oldVnode) {
     // 逻辑...
  }
   ...
})

 // 3. 注入组件选项
 Vue.mixin({
   created: function () {
     // 逻辑...
  }
   ...
})

 // 4. 添加实例方法
 Vue.prototype.$myMethod = function (methodOptions) {
   // 逻辑...
}
}

承上启下--可跳过直接看vue.use源码分析

初始化Vue的时候做了些什么--这个可以看之前的源码分析文章,从入口开始--下面是部分主要截图,

Vue.js 在整个初始化过程中,除了给它的原型 prototype 上扩展方法,还会给 Vue 这个对象本身扩展全局的静态方法,它的定义在 src/core/global-api/index.js 中:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
 get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
 get () {
   /* istanbul ignore next */
   return this.$vnode && this.$vnode.ssrContext
}
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
 value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

export default Vue

接下来进入initGlobalAPI,initGlobalApi方法主要是给Vue挂载一些全局静态方法,其中在initUse(Vue)这个方法,就是给Vue挂载use的静态方法。


export function initGlobalAPI (Vue: GlobalAPI) {
 // config
 const configDef = {}
 configDef.get = () => config
 if (process.env.NODE_ENV !== 'production') {
   configDef.set = () => {
     warn(
       'Do not replace the Vue.config object, set individual fields instead.'
    )
  }
}
 Object.defineProperty(Vue, 'config', configDef)

 // exposed util methods.
 // NOTE: these are not considered part of the public API - avoid relying on
 // them unless you are aware of the risk.
 Vue.util = {
   warn,
   extend,
   mergeOptions,
   defineReactive
}

 Vue.set = set
 Vue.delete = del
 Vue.nextTick = nextTick

 // 2.6 explicit observable API
 Vue.observable = <T>(obj: T): T => {
   observe(obj)
   return obj
}

 Vue.options = Object.create(null)
 ASSET_TYPES.forEach(type => {
   Vue.options[type + 's'] = Object.create(null)
})

 // this is used to identify the "base" constructor to extend all plain-object
 // components with in Weex's multi-instance scenarios.
 Vue.options._base = Vue

 extend(Vue.options.components, builtInComponents)

 initUse(Vue)
 initMixin(Vue)
 initExtend(Vue)
 initAssetRegisters(Vue)
}

Vue.use()

import { toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
 Vue.use = function (plugin: Function | Object) {
   const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
   //阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
   if (installedPlugins.indexOf(plugin) > -1) {
     return this
  }

   // additional parameters
   const args = toArray(arguments, 1)
   args.unshift(this)
   if (typeof plugin.install === 'function') {
     plugin.install.apply(plugin, args)
  } else if (typeof plugin === 'function') {
     plugin.apply(null, args)
  }
   installedPlugins.push(plugin)
   return this
}
}

自定义插件demo:

export default {
 async install(Vue, options) {
   // Element
   Vue.use(ElementUI, { size: "small", zIndex: 3000 });
   // Vue.use(ElementUI, { zIndex: 3000 })
   Vue.prototype.$jsPlumb = jsPlumb.jsPlumb;
   // 当前API地址
   Vue.prototype.$appAPI = process.env.VUE_APP_BASE_API;
   Vue.prototype.$formatNumber = formatNumber;
   Vue.prototype.$httpCode = httpCode;
   Vue.use(
     VueCodemirror /* {
          options: { theme: 'base16-dark', ... },
          events: ['scroll', ...]
        } */
  );
   Vue.directive("permission", permission);
}
}

假设我们通过Vue.use引入一个插件plugin(该插件可以暂时理解为一个变量或参数),即Vue.use(plugin);

  1. 给Vue挂载一个_installedPlugins的属性方法,初始为空数组,主要用来阻止多次注册相同插件

    在官网文档有这样一段话:Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。

  2. 通过installedPlugins.indexOf(plugin) > -1判断是否已经注册过,如果已经注册过,返回this。

  3. const args = toArray(arguments, 1),

    //执行了一个toArray方法,toArray接收了两个参数,arguments为Vue.use方法传入的参数集合,例如Vue.use(a,b,c),那么arguments类似于[a,b,c](说明:arguments只是类数组,并不是真正的数组)
    //此处因为我们只引入一个参数plugin,所以arguments类似于[plugin]。
    //toArray的作用是什么呢?看源码
    /**
    * Convert an Array-like object to a real Array.
    */
    export function toArray (list: any, start?: number): Array<any> {
     start = start || 0
     let i = list.length - start
     const ret: Array<any> = new Array(i)
     while (i--) {
       ret[i] = list[i + start]
    }
     return ret
    }

当执行toArray(arguments,1),会生成一个新数组ret,长度 = arguments.length-1,然后进行while循环,依次倒序把arguments的元素赋值给ret,因为ret比arguments长度少1,所以最终等同于arguments把除了第一个元素外的其余元素赋值给ret。toArray主要作用就是把类数组转化为真正的数组,这样才能调用数组的方法。因为此处我只引入一个plugin参数,即arguments=[plugin],所以toArray返回为空数组[]。

接着往下执行,args.unshift(this),等同于[].unshift(Vue),即args = [Vue];

4.然后执行

if (typeof plugin.install === 'function') {
     plugin.install.apply(plugin, args);
  } else if (typeof plugin === 'function') {
     plugin.apply(null, args);
  }

此处判断plugin的install是否为函数,如果为函数,立即执行pluign.install方法,install方法传入的参数为args内数组元素,即install接受的第一个参数为Vue.

如果plugin的install不是函数,那么判断plugin本身是否为函数,如果为函数,那么执行plugin函数,且参数为args内数组元素。

5.最后 installedPlugins.push(plugin),存储这个插件,在上面步骤二进行防止多次出发的操作。

综上所述,Vue.use的作用其实就是执行一个plugin函数或者执行pluign的install方法进行插件注册,并且向plugin或其install方法传入Vue对象作为第一个参数,use的其他参数作为plugin或install的其他参数。


以上是关于vue.use源码分析的主要内容,如果未能解决你的问题,请参考以下文章

vue.use源码分析

vuex源码简析

vue2源码-- 其他全局 api

vue2源码-- 其他全局 api

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段