找回密码
 立即注册
查看: 129|回复: 6

Vue2 选项data的合并策略

[复制链接]

9

主题

37

回帖

151

积分

注册会员

积分
151
发表于 2022-9-19 22:29:21 | 显示全部楼层 |阅读模式
在vue.js源码中, 选项 el,data,watch,props 等都有合并策略
在本帖中, 只分析 data 的合并策略
  1. /*对parent, child的key字段进行和合并,采取了不同的策略*/
  2.      function mergeField(key) {
  3.     var strat = strats[key] || defaultStrat;
  4.     /*把 strat(parent[key], child[key], vm, key)函数的返回值给对应的options[key]*/
  5.    options[key] = strat(parent[key], child[key], vm, key);
  6.      }
复制代码


回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:30:37 | 显示全部楼层
在 mergeOptions 选项合并策略中, strat(parent[key], child[key], vm, key) 函数的返回值, 赋值给 options[key]

可以先记住一个结论:

1. 无论是 vm 根实例, 还是子组件中 选项里的 data 最终都是函数.
2. 那么 data 是一个函数, 何时被的调用:加入到响应式系统或者数据初始化的时候,会被调用
接下来,分析选项 data 的合并过程, 下面是 strats.data 的自定义策略
  1. var strats = config.optionMergeStrategies;
  2.     strats.data = function ( parentVal, childVal, vm ) {
  3.             /*检测vm,区分vm根实例,还是子类(组件)*/
  4.             if (!vm) {
  5.                 /*处理组件的信息,vm的子类,选项的data就必须是function*/
  6.                 if (childVal && typeof childVal !== 'function') {
  7.                     warn( /*发出报错的信息*/
  8.                         'The "data" option should be a function ' +
  9.                         'that returns a per-instance value in component ' +
  10.                         'definitions.',
  11.                         vm
  12.                     );
  13.                     return parentVal
  14.                 }
  15.                 /*运行到这里:传递vm, 说明是子类,组件*/
  16.                 return mergeDataOrFn(parentVal, childVal)
  17.             }
  18.             /*运行到这里:传递vm, 说明是实例*/
  19.             return mergeDataOrFn(parentVal, childVal, vm)
  20.         };
复制代码


回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:31:29 | 显示全部楼层
在整个Vue源码选项合并过程中, 区分子类, 还是根实例, 通过判断参数是否传递了 vm.

1. 在子类中,子类 data 不是一个函数, 发生报错的信息,返回父组件的 parentVal, 如果是一个函数 直接调用 mergeDataOrFn 函数, 进行数据合并,不传入 vm
2. vm 是根实例, 调用 mergeDataOrFn 函数, 并传入 vm
mergeDataOrFn函数的源码如下
  1. function mergeDataOrFn( parentVal, childVal,vm) {
  2.         if (!vm) { /*子类*/
  3.             // in a Vue.extend merge, both should be functions
  4.             /* 通过 Vue.extend的子类,  父子的data都应该是一个函数*/
  5.             if (!childVal) {
  6.                 return parentVal
  7.             }
  8.             if (!parentVal) {
  9.                 return childVal
  10.             }
  11.             /*childVal, parentVal都没有传递, 运行到此结束 */
  12.             // when parentVal & childVal are both present,
  13.             // we need to return a function that returns the
  14.             // merged result of both functions... no need to
  15.             // check if parentVal is a function here because
  16.             // it has to be a function to pass previous merges.
  17.             /*能够运行到这里, childVal,parentVal都会有值,才进行合并*/
  18.             return function mergedDataFn() {
  19.                 /*将childVal,parentVal通过call来的调用, 把返回值纯对象当做参数传递给mergeData()函数*/
  20.                 return mergeData(
  21.                     typeof childVal === 'function' ? childVal.call(this, this) : childVal,
  22.                     typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal
  23.                 )
  24.             }
  25.         } else { /*vm根实例*/
  26.             return function mergedInstanceDataFn() {
  27.                 // instance merge
  28.                 var instanceData = typeof childVal === 'function'
  29.                     ? childVal.call(vm, vm)
  30.                     : childVal;
  31.                 var defaultData = typeof parentVal === 'function'
  32.                     ? parentVal.call(vm, vm)
  33.                     : parentVal;
  34.                 if (instanceData) {
  35.                     return mergeData(instanceData, defaultData)
  36.                 } else {
  37.                     return defaultData
  38.                 }
  39.             }
  40.         }
  41.     }
复制代码
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:32:02 | 显示全部楼层
在 mergeDataOrFn 代码中, 有这样的一段代码

  1. if (!childVal) {
  2.     return parentVal
  3. }
  4. if (!parentVal) {
  5.     return childVal
  6. }
复制代码

- 如果 childVal 没有值, 那么就直接返回 parentVal.
- 如果 childVal 有值, 而 parentVal 没有值, 就返回 childVal
- 两者都没有值, 下面的代码根本就不会执行. 两者都有值的情况下才进行下面的代码
在 childVal,parentVal 都有值的的情况下, 最终把 mergedDataFn() 函数返回, 没有进行合并
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:32:34 | 显示全部楼层
无论子组件还是根实例, 最终都调用mergeData进行终极合并, 瞬间产生了两个问题
为什么会返回一个 mergeData 函数?

1. 通过函数返回一个对象, 对象属于引用类型, 在内存不是同一份, 保证了每个组件的数据的独立性, 可以避免组件之间的相互影响

2. mergeData 为什么不在初始化的时候就合并好, 而是在调用的时候进行合并?

inject 和 props 这两个选项的初始化是先于 data 选项的,就保证了能够使用 props 初始化 data 中的数据.
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:32:54 | 显示全部楼层
接下来, 在看看最终的mergeData终极合并的函数

  1. /* 此时的to, form是纯对象*/
  2. function mergeData(to, from) {
  3.     /* form 没有值, 直接返回to*/
  4.     if (!from) {
  5.         return to
  6.     }
  7.     var key, toVal, fromVal;
  8.     /*获取from的key的集合*/
  9.     var keys = hasSymbol
  10.         ? Reflect.ownKeys(from)
  11.         : Object.keys(from);
  12.     /*对form的keys进行遍历*/
  13.     for (var i = 0; i < keys.length; i++) {
  14.         key = keys[i];
  15.         // in case the object is already observed...
  16.         /*将入到响应式系统的对象都将有__ob__属性*/
  17.         if (key === '__ob__') {
  18.             continue
  19.         }
  20.         toVal = to[key];
  21.         fromVal = from[key];
  22.         /*from 对象中的 key 不存在 to 对象中,则使用set函数为to对象设置 key 及相应的值*/
  23.         if (!hasOwn(to, key)) {
  24.             set(to, key, fromVal);
  25.          /* from对象中的key也在to对象中,且这两个属性的值都是纯对象则递归进行深度合并*/
  26.         } else if (toVal !== fromVal && isPlainObject(toVal) &&
  27.                     isPlainObject(fromVal)) {
  28.             mergeData(toVal, fromVal);
  29.         }
  30.     }
  31.     return to
  32. }
复制代码
回复

使用道具 举报

9

主题

37

回帖

151

积分

注册会员

积分
151
 楼主| 发表于 2022-9-19 22:33:13 | 显示全部楼层
终结合并只处理了两种情况

from 对象中的 key 不存在 to 对象中,则使用 set 函数为 to 对象设置 对应的 key 及对应的的值
from对象中的 key 也在 to 对象中,且这两个属性的值都是纯对象则递归进行深度合并
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|粤嵌技术交流空间

GMT+8, 2025-7-7 15:49 , Processed in 0.648616 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表