前言
在vue
项目中,难免会使用到三方插件,比如ElementUI
。那Vue
提供了一个Vue.use
方法,能够使用这些插件,并且Vue.Component
方法能够帮我们注册一个组件,下面我们就来一起看看吧。
Vue.use的依赖
- 安装插件:
npm i xx -D
。 - 引入插件:
import Xx from 'xx'
。 - 使用插件:
Vue.use(Xx)
。
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.use(ElementUI);
在Vue3
中:
import { createApp } from 'vue'
import MyPlugin from './plugins/MyPlugin'
const app = createApp({})
app.use(MyPlugin)
原理
-
参数:
{Object | Function} plugin
-
用法:
安装
Vue.js
插件。如果插件是一个对象,必须提供install
方法。如果插件是一个函数,它会被作为install
方法。install
方法调用时,会将Vue
作为参数传入。该方法需要在调用new Vue()
之前被调用。当
install
方法被同一个插件多次调用,插件将只会被安装一次。 -
源码:
export function initUse(Vue: GlobalAPI) {
// Vue.use是一个函数
Vue.use = function (plugin: Function | any) {
// 创建安装的插件数组,用来保存插件实例
const installedPlugins =
this._installedPlugins || (this._installedPlugins = [])
// 去重
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
const args = toArray(arguments, 1)
args.unshift(this)
// isFunction => typeof value === 'function'
// 检测如果有plugin的install方法, 如果plugin是一个对象
if (isFunction(plugin.install)) {
// 执行插件的install方法进行安装
plugin.install.apply(plugin, args)
// 这里直接当做plugin是一个函数,那么函数直接回当做install方法执行
} else if (isFunction(plugin)) {
plugin.apply(null, args)
}
// 保存插件
installedPlugins.push(plugin)
return this
}
}
所以Vue.use
的原理就是install
方法。
- 当
plugin
为对象的时候,必须要提供一个install
方法。 - 当
plugin
为函数的时候,则会被当做install
方法执行。 - 开发插件: 参考。
app.use
vue3
的app.use
代码过于简单,如下:
Vue.component的依赖
- 使用插件:
Vue.component('my-component', { /* ... */ })
。
在Vue3
中:
import { createApp } from 'vue'
const app = createApp({})
// 注册一个选项对象
app.component('my-component', {
/* ... */
})
// 得到一个已注册的组件
const MyComponent = app.component('my-component')
原理
-
参数:
{string} id
{Function | Object} [definition]
-
用法:
注册或获取全局组件。注册还会自动使用给定的
id
设置组件的名称
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件 (始终返回构造器)
var MyComponent = Vue.component('my-component')
- 示例vue2.x:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
// template
<template>
<div id="app">
<button-counter />
</div>
</template>
- 示例vue3.x:官方示例
- 源码:
Vue.component
方法执行,发生在初始化的时候,可以参考Vue源码解析系列(一) -- 初始化类new Vue。初始化的时候vue
会调用initGlobalAPI
进行各种初始化,其中component
方法就发生在initAssetRegisters
中。
initAssetRegisters
export function initAssetRegisters(Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
// ASSET_TYPES : ['component', 'directive', 'filter']
ASSET_TYPES.forEach(type => {
// 给Vue.component等方法赋值
Vue[type] = function (
id: string, // 组件名
definition?: Function | Object // 参数
): Function | Object | void {
// 如果没有参数Vue.component('my-component')
if (!definition) {
// 则取得是Vue.options.components[id]
return this.options[type + 's'][id]
} else {
// 如果有参数, Vue.component('my-component', {...})
// 检查组件名是否合法
if (__DEV__ && type === 'component') {
validateComponentName(id)
}
// Vue.component走这里
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
// 调用Vue.extend
definition = this.options._base.extend(definition)
}
...
// 绑定组件至Vue.options.components上
this.options[type + 's'][id] = definition
return definition
}
}
})
}
所以这就是Vue.component
的原理,
- 如果有第二参数,则通过
Vue.extend
返回组件,然后通过Vue.options.componnets
绑定组件。 - 如果没有第二参数,则直接用
Vue.options.components
绑定组件名。 new Vue
的时候能够使用这样的组件。- 我画了一个大致的图:
Vue.extend
那么Vue.extend
方法是基于Vue
的构造器创建一个子类实例,并返回这个实例,用法如下。
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 挂在到元素上
new Profile().$mount('#mount-point')
源码
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {} // 兼容有无Vue.component的第二参数
const Super = this // Vue
const SuperId = Super.cid // Vue.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// 获得组件名
const name =
getComponentName(extendOptions) || getComponentName(Super.options)
if (__DEV__ && name) {
validateComponentName(name)
}
// 创建VueComponent
const Sub = function VueComponent(this: any, options: any) {
this._init(options) // 初始化参数
} as unknown as typeof Component
// 创建原型对象
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub // 指向构造函数
Sub.cid = cid++
Sub.options = mergeOptions(Super.options, extendOptions) // 合并配置
Sub['super'] = Super //通过'super'属性指向Vue
if (Sub.options.props) {
initProps(Sub) // 初始化组件的props
}
if (Sub.options.computed) {
initComputed(Sub) // 初始化组件的computed
}
// 给实例组件绑定Vue的全局方法
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
//给实例组件绑定component、directive、filter方法
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// 绑定到实例组件的components里面
if (name) {
Sub.options.components[name] = Sub
}
// 更新时同步data、props、computed等
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// 缓存构造器
cachedCtors[SuperId] = Sub
// 返回自身VueComponent
return Sub
}
所以Vue.extend
就是基于Vue
构造器创建一个子实例对象并返回出去,也就是在Vue.component
的时候,有definition
会根据这一步走,产生子实例组件于Vue.options.compoents
绑定。
app.component
Vue3
的app.component
方法跟Vue2
处理逻辑差不多,先检测名字是否合法,再根据第二参数进行components
绑定。
总结
这一章我们主要是了解了Vue.use
与Vue.component
的用法和原理,在Vue2
里面基于initRender
会调用_c
函数,根据render
函数的执行加入到创建vnode
的过程中去,在vue3
里面则是根据setup
函数执行机制。下一章我们将会去了解一下关于Vue
更多的功能原理。