深入vue2响应式原理,在对象或数组新增属性无响应解决方法
前言该问题只存在vue2, 基于Object.defineProperty的特性,vue3中的proxy已经解决了该问题,但也存在兼容性问题,例如IE系统任意版本都不支持.vue2是如何追踪数据变化形成响应口水版:页面一进来会扫描数据,实行类似双向绑定,当初始时没有设定好属性,后面添加新属性,会存在数据中,但页面并不会进行响应同步(MVVM)专业版 :当你把一个普通的 JavaScript 对象传
前言
该问题只存在
vue2
, 基于Object.defineProperty的特性,vue3
中的proxy已经解决了该问题,但也存在兼容性问题,例如IE系统任意版本都不支持.
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,从而使它关联的组件重新渲染。
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);//age: 18,name: "杀生丸"
}
}
})
</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);//age: 18,name: "杀生丸"
}
}
})
</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(原对象/数组,需要设置的新属性, 需要设置的新值)
Vue.set(this.user, 'age', 18)
console.log(this.user);//age: 18,name: "杀生丸"
}
}
})
</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() {
// Object.assign方法的第一个参数是目标对象,后面的参数都是源对象
// Object.assign(目标对象,原对象, 新属性)
this.user = Object.assign({}, this.user, { skill: '铁碎牙', age: 18 })
console.log(this.user);//age: 18,name: "山竹",skill: "铁碎牙"
}
}
})
</script>
数组
方案一:切割替换原数组
//vm.items.splice(下标, 1, 新数组)
vm.items.splice(indexOfItem, 1, newValue)
方案二:Vue.set
//Vue.set(原数组,需要设置的下标, 需要设置的新值)
Vue.set(vm.items, indexOfItem, newValue)
更多推荐
所有评论(0)