1. 问题现象:当深拷贝遇上Vue计算属性
最近在Vue3项目中使用radashi的cloneDeep方法时,遇到了一个诡异的现象:当原始数据变化时,使用radashi的计算属性不会重新计算,而lodash版本却能正常工作。具体表现为:
- 点击"修改isClip属性"按钮时,两个计算属性都能正常更新
- 但点击"添加relationData关联数据"按钮时,只有lodash版本的计算属性会更新
- radashi版本的计算属性始终返回初始值,就像没检测到数据变化一样
这个现象让我百思不得其解——两个库不都是做深拷贝的吗?为什么行为差异这么大?通过Proxy对象进行属性访问跟踪后,真相逐渐浮出水面。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 根本原因解析:Vue3响应式系统的运作机制
2.1 Vue3的响应式原理
Vue3使用Proxy实现响应式系统,其核心机制是:
- 依赖收集:当计算属性函数执行时,所有被访问到的响应式属性都会被记录为"依赖"
- 触发更新:当这些依赖属性发生变化时,会通知计算属性重新计算
关键在于:只有被实际访问的属性才会成为依赖。如果某个属性在计算过程中从未被读取,那么它的变化不会触发计算属性更新。
2.2 深拷贝库的实现差异
通过Proxy调试,我们发现两个库的深拷贝实现有本质区别:
| 特性 | lodash.cloneDeep | radashi.cloneDeep |
|---|---|---|
| 属性访问方式 | 递归访问所有嵌套属性 | 可能使用结构性共享等优化算法 |
| 访问深度 | 完全深度访问 | 可能跳过部分嵌套属性 |
| 性能考量 | 保证数据完整性 | 优先考虑性能优化 |
| Vue依赖收集 | 完整收集 | 可能遗漏部分依赖 |
具体到我们的测试案例:
- lodash版本会访问
prop1、prop2、nested等所有业务属性 - radashi版本仅访问了
Symbol.toStringTag这样的内部元属性
3. 解决方案:自定义克隆策略
既然问题出在属性访问不完整,解决方案就是确保radashi在克隆时访问所有属性。radashi提供了自定义克隆策略的接口,我们可以这样实现:
3.1 对象克隆策略
javascript复制cloneObject: (obj, track, clone) => {
const newObj = track({}); // 避免循环引用
Object.keys(obj).forEach((key) => {
const value = obj[key]; // 关键:显式访问属性
newObj[key] = clone(value); // 递归克隆
});
return newObj;
}
3.2 数组克隆策略
javascript复制cloneArray: (parent, t
