浅谈Vue中的数据绑定的实现,以及Vue3.0的proxy

1,090 阅读2分钟

在开发过程中,我们时常会遇到这样一种情况:当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的到来。