Vue2.x属性更新限和Object.defineProperties

402 阅读1分钟

1、Vue2.x属性更新

image.png

1.1、结果描述:

  • 运行后当设置sex属性的时视图更新
  • 当设置money属性的时候视图不更新

问题分析:

  1. 当我们使用vm对象更新userlnfo中初始化就存在的属性时视图会自动触发更新,而当我们对userlnfo内部不存在的属性设置值的时候视图不更新,这是因为Vue2.x底层的数据响应式系统使用的是Object.defineProperty()来实现的

  2. Vue在初始化的时候会递归的将data选项中的属性绑定setter和getter,通过两者来观察属性的行为,一旦初始化完成便不会再次执行本操作,所以更改已知属性的时候Vue实例是有感知并执行默认处理行为的,当我们操作对象内部不存在的属性时由于其没有setter和getter,Vue将无法观察到这些属性的赋值和取值,视图更新操作也无法进行。

1.2、解决方案:

1.使用$set()更新后定义的属性

针对Vue2.x底层能力的限制,后定义的属性由于没有在初始化时绑定监听,所以Vue无法捕捉后定义属性的行为,为了弥补这种场景无法触发视图更新的问题,Vue在实例上提供了一个$set函数,用来辅助更新视图,其操作方式 如下:

vm.$set(要更新的对象,要设置的key,要设置的值)

image.png

2.使用$forceUpdate()强制渲染

除了$set之外,Vue还提供了一个API叫$forceUpdate 来实现视图的更新,它同样拴载在Vue的实例对象上,不同于setset,forceUpdate不需要指定要更新的属性,他会在执行的时候强制Vue实例重新执行一次渲染,这样视图层所有的内容都会重新执行一次更新,在这之前如果有后定义的属性进行更改同样也会体现在视图上,这种方式只适用于特殊场景否则会让渲染性能开销增大。

image.png

2、Object.defineProperties

<script>
        var obj = { name:123 }

        // 影子变量 给set函数用的
        var objTemp = { name:123 }

        // 对象 设置被监控的属性 配置项
        Object.defineProperty(obj,'name',{
            set(value){
                console.log("set函数被触发")
                console.log(value)
                // set函数中的this指向obj本身
                // 如果直接使用this.name或者obj.name赋值会触发set无限死循环直到栈溢出
                // obj.name = value
                
                objTemp.name = value    // 解决办法就是搞多一个影子变量 借壳
            },
            get(value){
                console.log("get函数被触发")
                console.log(value)
                // return value


                // objTemp
                return objTemp.name
            }

        })


    </script>

2.1、模拟vue2.x的data

<p>
    姓名:<input type="text" name="name" />
    年龄:<input type="text" name="age" />
    性别:<input type="text" name="sex">
</p>

<div id="text"></div>

<script>
        var onj = { name:"小明", age:18 }
        
        // 影子对象
        // var onjTemp = { name:"小明", age:18 }
        var onjTemp = {}   // 影子对象可以为空对象
 
        Object.keys(onj).forEach(item=>{
            // CSS [attribute=value] 选择器
            var ipt = document.querySelector(`input[name=${item}]`)

            // 为onj对象中的属性设置getter和setter
            Object.defineProperty(onj,item,{
                set(value){
                    console.log(value)  // sex没有被监听  所以无法被响应式 除非初始化时加上
                    onjTemp[item] = value
                    ipt.value = value    // 双向绑定 为了让数据改变从而修改输入框的数据
                    text.innerText = JSON.stringify(onjTemp)  
                },
                get(value){
                    return onjTemp[item]   // 从影子对象总取值
                }
            })


            ipt.oninput = function(e){
                onj[item] = e.target.value

            }
        })

        /*
            以上案例为了显示VUE在初始化时会递归我们的onj对象,如果对象中有初始化的属性时,
            就能确保该对象被监听
        */
    
    </script>

vue官网中也有说明:

image.png