字数 1081,阅读大约需 6 分钟
在我们使用 Vue2 的时候会遇到一个奇怪的问题。有时候我们修改了数据,页面却没有更新。这种情况让人困惑。今天我们就来聊聊这个问题的原因和解决方法。
为什么数据变了页面不更新?
我们先来看一个例子。假设我们有一个 Vue 实例:
new Vue({
data: {
user: {
name: '小明',
age: 18
}
}
})
如果我们直接修改 user 的属性:
this.user.name = '小红'
页面会正常更新。但是如果我们给 user 添加一个新属性:
this.user.gender = '男'
页面就不会更新。这是为什么呢?
Vue2 的响应式原理
要理解这个问题,我们需要了解 Vue2 的响应式原理。
Object.defineProperty 的作用
Vue2 使用 Object.defineProperty 来实现响应式。这个方法可以监听对象属性的变化。但是它有局限性:
-
• 只能监听已经存在的属性 -
• 不能监听新增的属性 -
• 不能监听属性的删除
Vue 的初始化过程
在 Vue 初始化的时候,它会遍历 data 中所有已经存在的属性,然后使用 Object.defineProperty 把它们变成响应式的。
// 简化的实现
function defineReactive(obj, key) {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
value = newVal
// 触发更新
}
})
}
这个过程中,Vue 只会处理初始化时存在的属性。后面新增的属性不会被处理。
this.$set() 的解决方案
Vue 提供了 this.$set() 方法来解决这个问题。
基本用法
this.$set(this.user, 'gender', '男')
这样添加属性,页面就会正常更新。
数组的情况
数组也有类似的问题。比如:
this.list[3] = '新元素' // 不会触发更新
this.list.length = 10 // 不会触发更新
这时候也需要使用 this.$set():
this.$set(this.list, 3, '新元素')
什么情况下需要使用 this.$set()?
我们需要在以下情况下使用 this.$set():
-
• 给对象添加新属性 -
• 修改数组的索引 -
• 修改数组长度
实际开发中的例子
假设我们有一个用户信息表单:
data() {
return {
form: {
name: '',
age: ''
}
}
}
用户提交后,我们需要添加一个提交时间:
// 错误的方式
this.form.submitTime = new Date()
// 正确的方式
this.$set(this.form, 'submitTime', new Date())
为什么 Vue 要这样设计?
有人可能会问,为什么 Vue 不自动处理新增的属性呢?
性能考虑
如果 Vue 要监听所有可能的属性变化,就需要:
-
• 在对象被访问时动态添加监听 -
• 或者预先为所有可能的属性添加监听
这两种方式都有性能问题。第一种会导致每次访问对象都要检查,第二种会创建大量无用的监听。
明确性
现在的设计让开发者明确知道什么时候需要特殊处理。这样可以避免意外的性能损耗。
替代方案
除了 this.$set(),我们还有其他选择。
提前声明所有属性
最好的方式是在 data 中提前声明所有可能用到的属性:
data() {
return {
user: {
name: '',
age: '',
gender: '' // 即使暂时不用也先声明
}
}
}
使用新对象
我们可以创建一个新对象来替换旧对象:
this.user = {
...this.user,
gender: '男'
}
常见误区
很多开发者容易陷入一些误区:
过度使用 this.$set()
有些开发者习惯在所有地方都使用 this.$set(),这是不必要的。只有在新增属性时才需要。
忘记处理数组
数组的修改也需要特别注意,特别是通过索引修改的情况。
Vue3 的改进
顺便提一下,Vue3 使用 Proxy 解决了这个问题。Proxy 可以监听整个对象,包括新增的属性。所以在 Vue3 中,我们不再需要 this.$set()。
但是理解 Vue2 的这个特性仍然很重要,因为:
-
• 很多老项目还在使用 Vue2 -
• 理解这个问题有助于我们更好地理解响应式原理 -
• 在面试中经常被问到
希望这篇文章能帮助你理解 this.$set() 的作用和用法。在实际开发中遇到类似问题时,就知道该怎么处理了。
🚀专注前沿技术拆解 | 每日 9:00 更新
👇 关注 | 点赞 | 分享,我们共同进化
🔥 热门文章推荐:

