Vue|Vue 源码分析 —— 选项合并策略(2)
选项合并策略分析(2)
[选项合并策略分析(1)]()
生命周期钩子函数合并策略
在 Vue 中,生命周期是一个很重要的知识点,我们可以在生命周期各个阶段做不同的事情。现在我们来看一下 Vue 生命周期钩子函数的合并策略
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})function mergeHook (
parentVal: ?Array,
childVal: ?Function | ?Array
): ?Array {
/**
* 合并策略
* 1. 子类不存在钩子选项时,使用父类钩子选项
* 2. 子类存在钩子选项,父类不存在钩子选项时,使用子类钩子选项
* 3. 子类和父类都存在钩子选项时,进行合并,将子类的钩子选项放在数组末尾,在执行钩子函数时,父类的钩子函数优先于子类执行
*/
const res = childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
return res
? dedupeHooks(res)
: res
}function dedupeHooks (hooks) {
const res = []
for (let i = 0;
i < hooks.length;
i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i])
}
}
return res
}
在这里解释一下上面的三元表达式的执行逻辑:
-
- 子类钩子函数不存在时,使用父类的钩子函数
-
- 子类的钩子函数存在父类钩子不存在,使用子类的钩子函数,(如果子类的钩子函数不是数组,则转换成数组)
-
- 子类和父类的钩子函数都存在时,将子类的钩子函数添加到数组末尾
举例说明
const parent = Vue.extend({
data(){
return {
a: 1
}
},
created(){
console.log("aaaaa")
}
})var sub = parent.extend({
// el:"#root",子类中不允许包含 el 选项
data(){
return {
b: 2
}
},
created(){
console.log("bbbbb")
}
})var subVm = new sub()
console.log(subVm.$data)
subVm.$mount("#root")
上面的例子中, parent 实例中的钩子函数 created 会与 Vue 构造器器中的钩子函数(此时为 undefined)进行合并,得到的结果为 [function created]
sub 实例会与 parent 中的钩子函数进行合并,此时 parent 的钩子函数选项为 [function created],合并后 sub 实例的 options 中的 created 钩子函数为 [function created, function created](其中第一个为 parent 中钩子函数,第二个为 sub 自身的钩子函数)。 最终得到的 sub 实例中 options 选项如下图:

文章图片
watch 选项合并
strats.watch = function (
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): ?Object {
// work around Firefox's Object.prototype.watch...
// 火狐浏览器在 Object 原型上拥有 watch ,进行兼容, export const nativeWatch = ({}).watch
if (parentVal === nativeWatch) parentVal = undefined
if (childVal === nativeWatch) childVal = undefined
/* istanbul ignore if */
if (!childVal) return Object.create(parentVal || null)
if (process.env.NODE_ENV !== 'production') {
// watch 选项必须是对象
assertObjectType(key, childVal, vm)
}
// 父类的 watch 选项不存在,则直接使用子类自身的 watch 选项
if (!parentVal) return childVal
const ret = {}
// 首先将父类的 watch 选项赋值给 ret 对象,然后便利子类的选项,
extend(ret, parentVal)
for (const key in childVal) {
let parent = ret[key]
const child = childVal[key]
// 先将父类 watch 选项中对 [key] 属性监听的 handler 方法转换成数组(如果存在的话)
if (parent && !Array.isArray(parent)) {
parent = [parent]
}
ret[key] = parent
? parent.concat(child)
: Array.isArray(child) ? child : [child]
}
return ret
}
对于 watch 选线的合并策略和生命周期钩子函数合并策略有些类似,只要父类选项中存在相同的观测字段,则和子类的选项合并成数组。由于 watch 选项有多种写法,合并之后的选项也存在多种情况
- 合并后的 watch 选项是一个对象,键为监听的属性名,值为回掉函数
- 合并后的 watch 选项是一个对象数组,数组每一项的键为监听的属性名,值为回掉函数
- 合并后的 watch 选项是一字符串/字符串数组(字符串为函数名,该方法在 methods 选项中进行定义)
const parent = Vue.extend({
data(){
return {
a: 1
}
},
watch:{
a: ['achange', 'change1']
},
methods:{
achange(){
console.log("achange")
},
change1(){
console.log('aaaaaa')
}
}})var sub = parent.extend({
// el:"#root",子类中不允许包含 el 选项
data(){
return {
b: 2
}
},watch:{
b: {
handler(newVal, oldVal){
console.log(newVal)
}
},
a:{
handler(newVal, oldVal){
console.log("sub watch a change")
console.log(newVal)
}
}
}
})
上面的例子中,最终合并之后, parent 中 watch 选项如下图所示:

文章图片
sub 中 watch 选项如下图所示

文章图片
props methods inject computed 合并策略
props、methods、inject、computed 这些选项的数据类型都是对象(props 和 inject 在选项规范话时已经转换成对象格式),合并策略是相同的,也相对比较简单。
如果父类选项不存在,则使用子类选项。如果子类和父类选项同时存在,使用子类选项覆盖父类选项
// 代码位置 core/util/options.js
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 合并策略
【Vue|Vue 源码分析 —— 选项合并策略(2)】provide 的合并策略和 data 合并策略一致,不同点就是 provide 没有规定必须时函数
推荐阅读
- SAP|SAP UI5 应用开发教程之三十九 - SAP UI5 应用出现白屏的一些常见错误和分析方法分享试读版
- Vue2.0源码学习(6)|Vue2.0源码学习(6) - 组件注册
- 一文带你解读Spring5源码解析|一文带你解读Spring5源码解析 IOC之开启Bean的加载,以及FactoryBean和BeanFactory的区别。
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表
- 助力中国开源码力榜,社区邀请正式开启!
- VUE页面局部组件刷新
- 【可视化-源码阅读】antvis|【可视化-源码阅读】antvis / g-base解读 - 1
- 关于electron+vue|关于electron+vue 安装并打包的爬坑记录
- vue中使用file-saver导出文件的全过程记录
- Python实现爬取天气数据并可视化分析