Vue中data的BUG和数组的变异

1,198 阅读1分钟

Vue的data的BUG

Object.defineProperty的问题

  • Object.defineProperty(obj,'n',{...})

  • 这里必须要有一个'n',才能监听&代理obj.n

  • 但如果开发者粗心大意,没有给n,或者给了n,但赋值时没有对n进行赋值

    import Vue from "vue/dist/vue.js";
    
    Vue.config.productionTip = false;
    
    new Vue({
      data: {
        obj: {
          a: 0 // obj.a 会被 Vue 监听 & 代理,a就是'n'
        }
      },
      template: `
        <div>
          {{obj.b}}
          <button @click="setB">set b</button>
        </div>
      `,
      methods: {
        setB() {
          this.obj.b = 1; 
        }
      }
    }).$mount("#app");
    

    这样写,Vue不会给警告,点击setB,页面中会显示 1 吗?

    答案是不会,因为Vue不会监听一开始不存在的obj.b

    解决办法

    1. 一开始声明了不就好了(这属实是废话,一开始声明好了,就不会有这个问题了)
    2. 使用Vue.setthis.$set
    new Vue({
      data: {
        obj: {
          a: 0 
        }
      },
      template: `
        <div>
          {{obj.b}}
          <button @click="setB">set b</button>
        </div>
      `,
      methods: {
        setB() {
          Vue.set(this.obj,'b',1)
          //或 this.$set(this.obj,'b',1)
        }
      }
    }).$mount("#app");
    

    这样,点击按钮,页面就会显示1

Vue.set和this.$set的作用

  • 新增key
  • 自动创建代理和监听(如果没有创建过)
  • 触发UI更新(但不会立即更新)

数组的变异

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      this.$set(this.array,3,'d');
    }
  }
}).$mount("#app");

这样点击按钮,没有办法添加‘d’,因为数组中index为3的位置,没有办法提前声明

于是乎,尤雨溪想了一招

methods: {
    setD() {
      this.array.push('d')
    }
  }

完美解决~

但如果我们打印出这个新数组,会发现这个新数组的原型被修改了

QQ图片20220105223431

也就是说,这个数组对象,传给Vue之后,Vue会篡改这个数组,它会在中间加一层原型

怎么篡改的?

其实就是加了一层原型链,

以push为例,大致思路如下:

class VueArray extends Array{
    push(...args){
        const oldLength = this.length//this就是当前数组
        super.push(...arg)//super.push 调用我父类上的push
        console.log('Vue知道 你push了')
        for(let i = oldLength; i<this.length;i++){
            vue.set(this,i,this[i])
        }
    }
}

Vue篡改push,然后对push的变化通过vue.set通知给Vue,那Vue就会知道这个数组变了

总结

对象中新增的key

  • Vue没有办法事先监听和代理
  • 要使用set来新增key,创建监听和代理,更新UI
  • 但最好提前把属性都写出来

数组中新增的key

  • 尤雨溪篡改了7个API方便对数组进行增删
  • 这7个API会自动处理监听和代理,并更新