Vue2中响应式原则缺陷

112 阅读1分钟

Vue2中响应式原则缺陷

  • Vue内部会递归的去循环vue中的data属性,会给每个属性都增加getter和setter,当属性值变化时会更新视图。
  • 重写了数组中的方法,当调用数组方法时会触发更新,也会对数组中的数据(对象类型)进行了监控
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <p>{{address}}</p>
    <p>{{name.a}}</p>
    <p>{{arr}}</p>
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.8/vue.js"></script>
  <script>
    let vm = new Vue({
      el: "#app",
      data() {
        return {
          name: {
            n: 'hs'
          },
          arr: [1, 2, 3],
          address:{}
        }
      }
    })
    // 一、不存在的属性,如果新增的话不能渲染视图,内部会采用defineProperty重新定义属性(getter setter)
    vm.name.a = 90

    // 第一种解决方式,通过用展开运算符获得原来的对象,再额外添加属性,从而触发更新
    vm.name = {
      ...vm.name,
      a: 91
    }

    // 第二种方式,通过$set触发内部更新
    vm.$set(vm.name, 'a', 92)
    vm.name.a = 93

    // 所以数据应该尽量少些嵌套关系

    // 二、数组只能通过改变数组的七个方法来实现更新视图,不能使用索引、长度
    vm.arr.push(4)
    // Vue额外提供的API
    vm.$set(vm.arr, 0, -1); // 修改数组内部使用的是splice方法 
    vm.$set(vm.address, 'number', '6-301'); // 新增属性通过内部会将属性定义成响应式数据        
    vm.$delete(vm.arr, 0);  // 删除索引,属性

  </script>
</body>

</html>

通过以上两点可以发现Vue中的缺陷:

  • 对象默认只监控自带的属性,新增的属性响应式不生效 (层级过深,性能差)
  • 数组通过索引进行修改 或者 修改数组的长度,响应式不生效

Vue额外提供的API:

vm.$set(vm.arr,0,100); // 修改数组内部使用的是splice方法 
vm.$set(vm.address,'number','6-301'); // 新增属性通过内部会将属性定义成响应式数据        
vm.$delete(vm.arr,0);  // 删除索引,属性

为了解决以上问题,Vue3.0使用Proxy来解决,不需要以上来就进行递归(性能好,但是兼容性差)

let obj = {
    name: {name: 'jw'},
    arr: ['吃', '喝', '玩']
}
let handler = {
    get(target,key){
        if(typeof target[key] === 'object' && target[key] !== null){
            return new Proxy(target[key],handler);
        }
        return Reflect.get(target,key);
    },
    set(target,key,value){ 
        let oldValue = target[key];
        if(!oldValue){
            console.log('新增属性')
        }else if(oldValue !== value){
            console.log('修改属性')
        }
        return Reflect.set(target,key,value);
    }
}
let proxy = new Proxy(obj,handler);

代理 get、set方法,可以实现懒代理。并且兼容数组索引和长度变化