深入vue2响应式原理,在对象或数组新增属性无响应
解决方法
前言
该问题只存在vue2, 基于Object.defineProperty的特性,vue3中的proxy已经解决了该问题,但也存在兼容性问题,
例如IE系统任意版本都不支持.
![image-20230524090139099]()
vue2是如何追踪数据变化形成响应
口水版:页面一进来会扫描数据,实行类似双向绑定,当初始时没有设定好属性,后面添加新属性,会存在数据中,但页面并不会进行响应同步(MVVM)
专业版 :
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
![image-20230524090114595]()
vue2响应的注意点
由于 JavaScript 的限制(其实是Object.defineProperty),Vue 不能检测数组和对象的变化。
比方日常开发中遇到需要在对象中添加新的属性,属性已经存在数据中,但页面并不会立即更新.
如下,点击按钮时,'山竹'会变成'杀生丸',但age会在数据中,不会立刻出现在页面
<div id='app'>
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<button @click='changeName'>改变data中name属性</button>
<button @click='addAge'>给data添加age属性</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data() {
return {
user: { name: '山竹' }
}
},
methods: {
changeName() {
this.user.name = '杀生丸'
},
addAge() {
this.user.age = 18
console.log(this.user);
}
}
})
</script>
解决方案
其解决方法多种多样,这里举例几种,但根本的中心点就是使对象/数组发生变更,触发watcher重新渲染
对象
方案一:初始时设定
如下,在一开始的时候就预设可能需要添加的属性, age: ''
<div id='app'>
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<button @click='changeName'>改变data中name属性</button>
<button @click='addAge'>给data添加age属性</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data() {
return {
user: { name: '山竹', age: '' }
}
},
methods: {
changeName() {
this.user.name = '杀生丸'
},
addAge() {
this.user.age = 18
console.log(this.user);
}
}
})
</script>
方案二:调用Vue.set方法
Vue.set(原对象,需要设置的新属性, 需要设置的新值)
<div id='app'>
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<button @click='changeName'>改变data中name属性</button>
<button @click='addAge'>给data添加age属性</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data() {
return {
user: { name: '山竹' }
}
},
methods: {
changeName() {
this.user.name = '杀生丸'
},
addAge() {
Vue.set(this.user, 'age', 18)
console.log(this.user);
}
}
})
</script>
方案三:创建一个新的对象,替换原对象
这种方法可以用于需要添加多个新属性,再把原对象与新属性合并到新对象中 Object.assign(目标对象,原对象, 新属性)
<div id='app'>
<p>{{ user.name }}</p>
<p>{{ user }}</p>
<button @click='changeName'>改变data中name属性</button>
<button @click='add'>给data添加新属性</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data() {
return {
user: { name: '山竹' }
}
},
methods: {
changeName() {
this.user.name = '杀生丸'
},
add() {
this.user = Object.assign({}, this.user, { skill: '铁碎牙', age: 18 })
console.log(this.user);
}
}
})
</script>
数组
方案一:切割替换原数组
vm.items.splice(indexOfItem, 1, newValue)
方案二:Vue.set
Vue.set(vm.items, indexOfItem, newValue)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!