使用方法
-
如何使用
扩展
-
允许你扩展另一个组件,可以是一个简单的对象或构造函数,而不需要使用扩展,这主要是为了方便扩展单文件组件,这与
mixins
类似。 -
main 函数适用于同一个组件,但是是在一页上使用,而不是全局
-
var CompA = { ... } // 在没有调用 `Vue.extend` 时候继承 CompA var CompB = { extends: CompA, }
-
-
如何使用Mixins
-
接收一组混合对象,这些混合对象可以包含徐翔实例作为普通实例对象,这些选项将组合成最终选项。
-
Mixin的hook按照传递的顺序依次调用,在组件自己的hook被调用之前调用
-
var mixin = { created: function () { console.log(1) } } var vm = new Vue({ created: function () { console.log(2) }, mixins: [mixin] }) // => 1 // => 2
-
实现原理
- 源码分析
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
...格式化一些属性
if (!child._base) {
if (child.extends) { // 如果存在extends方法将组件进行合并 child.extends 可以是个构造函数和对象
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) { // child.mixins[i] 是个对象 ,将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
}
连接函数中最重要的部分是mergeField
函数中的strats[key]
。您必须检查option.js
特定的加入方法才能查看特定属性的特定方法。以下信息
// strats
const strats = config.optionMergeStrategies
-
el
连接方法-
if (process.env.NODE_ENV !== 'production') { strats.el = strats.propsData = function (parent, child, vm, key) { if (!vm) { warn( `option "${key}" can only be used during instance ` + 'creation with the `new` keyword.' ) } return defaultStrat(parent, child) } }
- 仅适用于开发环境。如果
vm
没有通过,则会显示一条信息,并使用defaultStrat
方法直接将child
上的数据替换为 中的数据
- 仅适用于开发环境。如果
-
-
如何组合
data
-
strats.data = function ( // 将两个对象数据进行合并 parentVal: any, childVal: any, vm?: Component ): ?Function { if (!vm) { if (childVal && typeof childVal !== 'function') { process.env.NODE_ENV !== 'production' && warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm ) return parentVal } return mergeDataOrFn(parentVal, childVal) } return mergeDataOrFn(parentVal, childVal, vm) }
- 如果没有传入对象:如果子元素不是函数,则会在开发环境中调用,并返回父元素;或者合并父元素和子元素
- 如果传递对象
vm
:合并父子元素
-
//mergeDataOrFn 合并方法 export function mergeDataOrFn ( parentVal: any, childVal: any, vm?: Component ): ?Function { if (!vm) { if (!childVal) { return parentVal } if (!parentVal) { return childVal } return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } } else { return function mergedInstanceDataFn () { // instance merge const instanceData = typeof childVal === 'function' ? childVal.call(vm, vm) : childVal const defaultData = typeof parentVal === 'function' ? parentVal.call(vm, vm) : parentVal if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData } } } }
- 否
vm
对象:任何父子元素参数为空,将返回另一个元素;或者父子元素方法完成后会指向对象,对象会被合并并返回 当 - 有传入对象
vm
时:父子函数执行后的结果合并返回给
- 否
-
// mergeData 具体的对象合并方法 function mergeData (to: Object, from: ?Object): Object { if (!from) return to let key, toVal, fromVal const keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from) for (let i = 0; i < keys.length; i++) { key = keys[i] // in case the object is already observed... if (key === '__ob__') continue toVal = to[key] fromVal = from[key] if (!hasOwn(to, key)) { set(to, key, fromVal) // 将to中不存在的from设置到to对象中 } else if ( toVal !== fromVal && // 如果两个数据不相同且都是对象的话,然后将元素中的所有进行递归的绑定 isPlainObject(toVal) && isPlainObject(fromVal) ) { mergeData(toVal, fromVal) } } return to }
- 获取
from
中对象的所有属性,并对属性进行循环操作。如果对象中不存在该属性,则直接绑定到对象to
;如果to
对象中已存在该属性,则默认不绑定该属性。如果两个对象的属性值不同且所有对象都相同时,则会循环合并对象属性。
- 获取
-
生命周期耦合方法
-
export const LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated', 'errorCaptured', 'serverPrefetch' ] LIFECYCLE_HOOKS.forEach(hook => { strats[hook] = mergeHook })
-
- 基本上是将父子函数插入数组并返回它以进行连接
-
-
|export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { ...格式化一些属性 if (!child._base) { if (child.extends) { // 如果存在extends方法将组件进行合并 child.extends 可以是个构造函数和对象 parent = mergeOptions(parent, child.extends, vm) } if (child.mixins) { // child.mixins[i] 是个对象 ,将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 }
directive
|filter
如何组合-
export const ASSET_TYPES = [ 'component', 'directive', 'filter' ] ASSET_TYPES.forEach(function (type) { strats[type + 's'] = mergeAssets })
-
function mergeAssets ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): Object { const res = Object.create(parentVal || null) if (childVal) { process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm) return extend(res, childVal) // 将子对象中的数据传入到父对象中 } else { return res } }
-
export function extend (to: Object, _from: ?Object): Object { for (const key in _from) { to[key] = _from[key] } return to }
- 如果数据在
childVal
中,则中的所有参数都会绑定在parentVal
中,但对于相同属性的数据,childVal
将覆盖parentVal
中的数据
- 如果数据在
-
-
如何组合
watch
-
strats.watch = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { ...格式验证等 const ret = {} extend(ret, parentVal) for (const key in childVal) { let parent = ret[key] const child = childVal[key] if (parent && !Array.isArray(parent)) { parent = [parent] } ret[key] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child] } return ret }
childVal
中的属性将循环绑定到parentVal
。如果parentVal
中存在相同属性,则将parentVal和
childVal`中对应的属性值插入到数组中,作为 的属性值
-
-
props
|methods
|inject
|computed
连接方法-
strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { if (childVal && process.env.NODE_ENV !== 'production') { assertObjectType(key, childVal, vm) } if (!parentVal) return childVal const ret = Object.create(null) extend(ret, parentVal) if (childVal) extend(ret, childVal) return ret }
- 联接方式是后者直接覆盖前者相同属性的数据,如果不相同则可以直接链接。
-
-
如何搭配
provide
-
strats.provide = mergeDataOrFn
- 的组合方式与
data
相同
- 的组合方式与
-
// config.optionMergeStrategies
export type Config = {
optionMergeStrategies: { [key: string]: Function };
};
export default ({
optionMergeStrategies: Object.create(null),
}: Config)
// defaultStrat 直接覆盖
const defaultStrat = function (parentVal: any, childVal: any): any {
return childVal === undefined
? parentVal
: childVal
}
总结
data
、provide
:聚合时,如果目标对象不存在,则属性将直接与目标对象关联;但是,如果目标对象已经存在,则不会进行绑定,但是如果属性对象的属性两个值都是对象,则必须对属性值对象进行递归串联操作。
生命周期
:拼接时,将目标对象的钩子函数属性值替换为一个数组,该数组存储了目标对象对应的函数和要拼接的对象对应的函数
component
| directive
| filter
, props
| methods
| inject
| computed
:组合后,物品将被绑定。属性是循环的,如果目标对象不存在则直接绑定,如果存在则直接覆盖
watch
:合并时,如果目标与合并对象属性相同,则相同属性会合并,不同属性不会合并。串联的主要方法是将属性值替换为一个数组,该数组包含目标对象的方法和要串联的对象对应的函数
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。