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);
给Vue挂载一个_installedPlugins的属性方法,初始为空数组,主要用来阻止多次注册相同插件
在官网文档有这样一段话:
Vue.use
会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。通过installedPlugins.indexOf(plugin) > -1判断是否已经注册过,如果已经注册过,返回this。
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源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段