最近在写一个简单的列表修改功能时,被一个小bug卡了快半小时,明明逻辑看着没问题,控制台打印也正常,页面就是不更新。现在把完整踩坑过程记录下来,也给自己提个醒。
我的需求很简单:页面展示一个数组列表,点击按钮修改列表第一项的值,视图同步更新。
按照原生JS的习惯,我直接写下标修改,代码如下:
<template>
<div class="container">
<h3>数组列表</h3>
<div class="item" v-for="(item, index) in numList" :key="index">
{{ item }}
</div>
<button @click="changeFirst">修改第一项</button>
</div>
</template>
<script>
export default {
data() {
return {
numList: [11, 22, 33, 44]
}
},
methods: {
changeFirst() {
this.numList[0] = 999
console.log("修改后的数组:", this.numList)
}
}
}
</script>
<style scoped>
.container {
padding: 20px;
}
.item {
margin: 10px 0;
font-size: 16px;
}
button {
padding: 8px 16px;
cursor: pointer;
}
</style>
写完我直接运行,点下按钮,结果人傻了:
控制台清清楚楚输出:修改后的数组:[999, 22, 33, 44]
但页面上,第一项依旧是 11,一动不动。
我的排查心路历程(真实踩坑过程)
看到这个现象我第一反应是:事件没绑定上?
• 重新检查 click 事件名,没错。
• 加了 console 发现事件确实触发了,数据也确实变了。
然后怀疑:是不是 key 重复导致渲染问题?
• 把 index 改成唯一 id,依旧不更新。
又怀疑:是不是被 CSS 隐藏了?
• 打开控制台看元素,内容还是旧的。
当时甚至一度怀疑是 Vue 出bug了,或者热更新坏了,反复重启项目、清缓存,结果还是一样。
后来实在没办法,去翻之前的笔记,才猛然想起:
Vue 不能检测通过数组下标直接修改的数据变化!
瞬间恍然大悟,刚才一顿操作全是无用功,根本方向就错了。

原因说明
Vue2 的响应式是基于 Object.defineProperty 实现的,它能监听对象的读写,但无法监听数组下标赋值和数组长度修改。
Vue 只对数组的这些方法做了响应式处理:
push、pop、shift、unshift、splice、sort、reverse
直接写 arr[index] = xxx 不会触发劫持,所以数据变了,视图不更新。
正确的几种写法
1. 使用 this.$set(推荐)
changeFirst() {
this.$set(this.numList, 0, 999)
}
2. 使用 splice
changeFirst() {
this.numList.splice(0, 1, 999)
}
3. 直接替换新数组
changeFirst() {
this.numList = [999, 22, 33, 44]
}
改完之后,页面立刻正常更新,问题解决。
总结
以后再遇到这种情况:
• 数据在控制台正常更新
• 页面不渲染
• 控制台不报错
• 代码逻辑看起来没问题
优先排查是不是:
• 直接用下标改数组
• 给对象新增了属性没有用 $set
• 修改了数组长度
这都是Vue最经典的响应式坑,新手几乎必踩,我也算又加深了一遍印象。

277

被折叠的 条评论
为什么被折叠?



