记一次 vue3 数组不响应问题的排查,对象也有可能不响应

最近在编程时,遇到了 vue3 数组更新却没有响应的问题,解决后,决定把对这次的问题总结记录下来。

代码重现

项目的代码太复杂了,我做了一个超精简版本的重现代码:

{{ data.list }}

添加

item-service 代码:

// 条目列表缓存对象let _items: string[] | undefined = undefinedtype Listener = (items: string[]) => voidconst listeners: Array = []export async function getItems(): Promise { if (_items) { return _items } // 从服务器请求数据 _items = await doAjax() return _items}export async function addItem(item: string) { const items = await getItems() items.push(item) listeners.forEach(listener => listener(items))}export async function onItemsChange(listener: Listener) { listeners.push(listener)}

item-service 中缓存了列表对象,然后组件中一直使用 item-service 中缓存的这个列表给 data 赋值 ,问题就出在这。

程序调试

vue3 的响应式是通过 Proxy 实现的,我在 onItemsChange 回调时做了调试,下面是 Proxy handler 的 set 方法的调试过程。为了方便查看,调试中没有被执行到的代码都注释掉了,并且加了一些说明。

// 注释掉的代码都是调试过程中没有被执行的代码function set(target, key, value, receiver) { // 代理 handler 的 set 方法参数说明: // target 原始对象,getItems() 返回的 list // key 属性名称,‘list’ // value 要设置的值,onItemsChange 回调的 list 对象 // receiver 最初被调用的对象,通常是 proxy 本身,这里就是 data.list, list的代理对象 let oldValue = target[key]; // 使用 shallowReactive 的情况下 shallow 标记是 true ,这里是 false if (!shallow && !isReadonly(value)) { value = toRaw(value); oldValue = toRaw(oldValue); // export const isArray = Array.isArray // !isArray(target) 返回 false if (!isArray(target) && isRef(oldValue) && !isRef(value)) { // oldValue.value = value; // return true; } } const hadKey = isArray(target) && isIntegerKey(key) // isIntegerKey(key) 返回 false,下行不执行 // ? Number(key) !Object.is(value, oldValue) // value 和 oldValue 是同一个 list 数组对象, hasChanged 返回 false // trigger(target, “set” /* SET */, key, value, oldValue); } } return result;}

可以看到没有触发任何更新,因此没有响应。

原因总结

原因总算确定了,其实就是因为被代理的数组,和要赋值的数组是同一个数据,在 Proxy 的回调中判定值没有改变,没有触发更新。

// data.list 是 list 的代理对象,将 data.list 提取原始值(toRaw)就是 listdata.list = list

处理的方法也比较简单,将数组简单的克隆下,就没有问题了。

getItems().then((res) => data.list = […res]).catch(console.error)onItemsChange((list) => data.list = […list])

还可以将 item-service 中返回的列表直接克隆。

我这种情况是由于缓存数据共享对象引起的,对于需要共享数据的项目,还可以使用状态管理组件,vuex 或 pinia。

对象是否也有同样问题?

既然是因为复用数组,代理回调因为值没有改变最终没有触发更新,那么对象是否也存在这样的问题呢,我写了个简单的代码验证了下:

{{ data.user }}

change age

答案是肯定的,问题仍然存在。如果存在这种情况,可以将对象克隆(使用 Object.assign() 或其它的方法)得到一个新的对象,再给响应式数据赋值来解决。

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月14日
下一篇 2022年6月14日

相关推荐

  • 淘特东西那么便宜怎么赚钱(淘特的东西比淘宝便宜吗)

    随着拼多多的火爆,越来越多类似平台出现,淘宝就推出了淘宝特价版来对标拼多多,里面的东西也是价格非常便宜,那么淘特东西那么便宜怎么赚钱?淘特东西为什么那么便宜?下面小编为大家带来淘特…

    2022年11月8日
  • 2022年TikTok跨境电商趋势报告(全球直播电商趋势+用户分析)

    来源:山西数据流量生态园大数据研究中心 【报告看点】 1、全球直播电商趋势 2、全球直播电商用户分析 3、TikTok平台分析 报告领取方式见文末 完整电子版报告共计25页 希望获…

    2022年6月21日
  • OC底层原理(二).内存分配与内存对齐

    从内存分配开始 在上一篇的流程图中,我们看到最后的流程中,在_class_createInstanceFromZone,我们分为三步: 1、size = cls->insta…

    2022年7月3日
  • iQOO指纹解锁自定义图案大全

    干货分享!今天来了解iQOO指纹图标那些技巧。 最近网上很火的iQOO手机能自定义更换喜欢的锁屏图标。OriginOS Ocean系统可以自己高度自定义指纹样式,但可能不会知道一些…

    2022年6月29日
  • MYSQL存储过程即常用逻辑知识点总结

    Mysql存储过程 1.创建存储过程语法(格式) DELIMITER $CREATE PROCEDURE 存储过程名A(IN 传入参数名a INT,IN 传入参数名b VARCHA…

    2022年6月18日
  • 开发版iOS 16先别升级,里面的bug可不少

    最近估计对科技行业感兴趣的朋友们应该都被苹果 WWDC22 开发者大会的东西给刷屏了,各大平台纷纷推出有关内容,让你想不看都不行,而虽然说此次的WWDC 内容比较多,但是大家最关注…

    2022年6月23日
  • 最新分红公告 2022年7月30日最新公布股票分红公告

    代码 名称 现金分红比例 股息率% 股权登记日 除权除息日 最新公告日期 600673 东阳光 10派2.7 2.34 – – 7月30日 688008 澜…

    2022年8月4日
  • 新型机器人比跳蚤还小!组装技术获多篇顶刊封面

    来源:【科学网】 外形像螃蟹的微型机器人 受访者供图 一个比跳蚤还小的“螃蟹”机器人站在硬币的侧边上。 受访者供图 史上最小的遥控步行机器人来了,它们只有半毫米宽,体积还不及一个跳…

    2022年7月21日
  • 夏洛特黄蜂队全队员数据信息

    Miles Bridges(迈尔斯·布里奇斯) 0号 前锋 Miles Bridges(迈尔斯·布里奇斯) 0号 前锋 场均得分:20.2 场均篮板:7.0 场均助攻:3.8 球员…

    2022年8月20日
  • 有了这个开源工具后,我五点就下班了

    前言   「一个优秀的开发者,一定是会利用各种工具来提升自己的开发效率。」 前段时间,博主在Gitee/Github开源了一个提升开发效率的工具,工具内集成了各种常用工具如csv、…

    2022年9月25日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息