前言
这周主要学习Vue全局API
- Vue.use
- Vue.mixin
- Vue.component
- Vue.filter
- Vue.directive
- VUe.extend
- Vue.set
- Vue.delete
- Vue.nextTick
入口
src/core/global-api/index.js
* 初始化 Vue 的众多全局 API,比如:
* 默认配置:Vue.config
* 工具方法:Vue.util.xx
* Vue.set、Vue.delete、Vue.nextTick、Vue.observable
* Vue.options.components、Vue.options.directives、Vue.options.filters、Vue.options._base
* Vue.use、Vue.extend、Vue.mixin、Vue.component、Vue.directive、Vue.filter
*
*/
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.'
)
}
}
// Vue.config
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 / delete / nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <>(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
// 在 Vue.options.components 中添加内置组件,比如 keep-alive
extend(Vue.options.components, builtInComponents)
// Vue.use
initUse(Vue)
// Vue.mixin
initMixin(Vue)
//Vue.extend
initExtend(Vue)
// Vue.component/directive/filter
initAssetRegisters(Vue)
}
Vue.use
src/core/global-api/use.js
/**
* 定义 Vue.use,负责为 Vue 安装插件,做了以下两件事:
* 1、判断插件是否已经被安装,如果安装则直接结束
* 2、安装插件,执行插件的 install 方法
* @param {*} plugin install 方法 或者 包含 install 方法的对象
* @returns Vue 实例
*/
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}t
// 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
}
}
Vue.mixin
src/core/global-api/mixin.js
/**
* 定义 Vue.mixin,负责全局混入选项,影响之后所有创建的 Vue 实例,这些实例会合并全局混入的选项
* @param {*} mixin Vue 配置对象
* @returns 返回 Vue 实例
*/
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
* Merge two option objects into a new one.
* Core utility used in both instantiation and inheritance.
* * 合并两个选项,出现相同配置项时,子选项会覆盖父选项的配置
*/
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
Vue.component、Vue.filter、Vue.directive
这三个api放一起介绍
* Create asset registration methods.
* 定义 Vue.component、Vue.filter、Vue.directive 这三个方法
* 这三个方法所做的事情是类似的,就是在 this.options.xx 上存放对应的配置
* 比如 Vue.component(compName, {xx}) 结果是 this.options.components.compName = 组件构造函数
* ASSET_TYPES = ['component', 'directive', 'filter']
*/
const ASSET_TYPES = ['component', 'directive', 'filter']
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
Vue.extend
src/core/global-api/extend.js
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
* /**
* 基于 Vue 去扩展子类,该子类同样支持进一步的扩展
* 扩展时可以传递一些默认配置,就像 Vue 也会有一些默认配置
* 默认配置如果和基类有冲突则会进行选项合并(mergeOptions)
* 例如我们在vue中写jsx
*/
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
/**
* 利用缓存,如果存在则直接返回缓存中的构造函数
* 什么情况下可以利用到这个缓存?
* 如果你在多次调用 Vue.extend 时使用了同一个配置项(extendOptions),这时就会启用该缓存
*/
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 选项合并,合并 Vue 的配置项到 自己的配置项上来
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
// 初始化 computed,将 computed 配置代理到 Sub.prototype 对象上
// 在组件内可以通过 this.computedKey 的方式访问
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
Vue.set
src/core/global-api/index.js
Vue.set = set
set
src/core/observer/index.js
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
* * 通过 Vue.set 或者 this.$set 方法给 target 的指定 key 设置值 val
* 如果 target 是对象,并且 key 原本不存在,则为新 key 设置响应式,然后执行依赖通知
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 更新数组指定下标的元素,Vue.set(array, idx, val),通过 splice 方法实现响应式更新
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 更新对象已有属性,Vue.set(obj, key, val),执行更新即可
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
// 给对象定义新属性,通过 defineReactive 方法设置响应式,并触发依赖更新
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
Vue.delete
src/core/global-api/index.js `Vue.delete = del
del
src/core/observer/index.js
* Delete a property and trigger change if necessary.
* 通过 Vue.delete 或者 vm.$delete 删除 target 对象的指定 key
* 数组通过 splice 方法实现,对象则通过 delete 运算符删除指定 key,并执行依赖通知
*/
export function del (target: Array<any> | Object, key: any) {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
if (!hasOwn(target, key)) {
return
}
delete target[key]
if (!ob) {
return
}
ob.dep.notify()
}
Vue.nextTick
这个可以看往期的文章
总结
这些api有一些还是比较常用的,我们不仅要知道怎么用,还需要掌握一定的原理,所以相对来说还是比较有用的。