在开发过程中,我们时常会遇到这样一种情况:当vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时,向对象中添加新的属性,如果更新此属性的值,是不会更新视图的。
根据官方文档定义:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。 受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应。
当然针对这种情况,官方也提供了解决方案,如下: Vue 不允许在已经创建的实例上动态添加新的根级响应式属性 (root-level reactive property)。然而它可以使用Vue.set(obj, key, val)方法将响应属性添加到嵌套的对象上: Vue.set(vm.obj, 'e', 0) 您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名: this.$set(this.obj, 'e', 2)
那么为什么会这样呢?还是要从Vue实现数据绑定的原理说起(Object.defineProperty),假设我们把Vue数据绑定精简为下列代码:
<div>
<h3>展示姓名:<span id="name"></span></h3>
<p>输入姓名:<input type="text" oninput="inputHandler('name', this.value)" /></p>
<h3>展示电话号码:<span id="phone"></span></h3>
<p>输入电话号码:<input type="text" oninput="inputHandler('phone', this.value)" /></p>
</div>
<script type="text/javascript">
// 需要监听的对象 ----
var obj = {
name: null
}
// 定义监听 - Object.defineProperty的实现
for(let key in obj) {
let val = obj[key];
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return val;
},
set(newValue) {
if(val === newValue) {
return;
}
document.getElementById(key).innerText = newValue;
val = newValue;
}
})
}
var inputHandler = function(key, value) {
obj[key] = value;
}
</script>
那么重点来了,我们有没有办法实现新增属性也能自动绑定呢?答案当然是有,也就是即将推出的Vue3.0也采用的ES6的新API - Proxy,用新的Proxy 改些后的代码如下:
<div>
<h3>展示姓名:<span id="name"></span></h3>
<p>输入姓名:<input type="text" oninput="inputHandler('name', this.value)" /></p>
<h3>展示电话号码:<span id="phone"></span></h3>
<p>输入电话号码:<input type="text" oninput="inputHandler('phone', this.value)" /></p>
</div>
<script type="text/javascript">
// 需要监听的对象 ----
var obj = {
name: null
}
obj = new Proxy(obj, {
get: function(target, prop) {
console.log('proxy get:', target, prop);
},
set: function(target, prop, value) {
document.getElementById(prop).innerText = value;
target[prop] = value;
console.log('proxy:', target, prop, value);
}
})
var inputHandler = function(key, value) {
obj[key] = value;
}
</script>
运行起来看看,是不是不使用$set,就能实现新增属性的绑定呢?期待Vue3.0的到来。