《数据响应式-3》

115 阅读2分钟

现在我们知道了,Vue对data的所有属性进行了监控和代理,使得,对data的属性的更改,vm都要知道。知道了,就会对UI进行渲染。

那到底什么是数据响应式呢?

首先,什么是响应?

我打你一拳,你会喊疼,这就是响应。你就是响应式的。也就是说,外界在你身上做了操作,你能作出反应,那就是响应式的。

Vue的data就是响应式的

因为 const vm=new Vue({data{n:0}})之后,我不管是用this.n 还是myData.n修改,UI视图中的n就会跟着改变,就会响应我。这就是数据响应式。 Vue是通过Object.defineProperty 来实现数据响应式的。

响应式网页就是如果我改变窗口大小,网页内容会作出响应。

Vue的data有一个bug

Vue是使用Object.defineProperty来监听并且代理data的

Object.defineProperty(obj,'n',(...)

就是说,必须给我一个'n'这个属性,我才能对n监听然后代理。

那如果程序员在一开始没有给n ,该怎么办

示例1

如果我在data里什么都不写,还要在视图渲染n。结果是视图里什么都没有。控制台会报一个警告。说:'n'没有被定义在实例上,但是渲染期间却被引用了。

说明,一个没有定义的数据是不能使用的。

示例2

既然警告说,你要渲染的数据没定义,我没找到。那我就绕过这个问题,定义一个:

new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; //请问,页面中会显示 1 吗?
    }
  }
}).$mount("#app");

首先:data里又嵌套了一层obj,我定义了obj.a。于是Vue就会监听&代理obj.a。(和obj,我写的都会监听)

但是,我在template里要渲染的是obj.b。虽然obj.b还是没有定义,却没有警告了。因为Vue要渲染的时候,只会检查第一层。看到obj定义了,就不会再往里检查了。

我通过这种方法绕过了警告

点击按钮,会把this.obj.b赋值为1。那么页面会显示1吗?结果是不会。

因为定义的是obj.a,所以Vue只会监听你给的属性。obj里有什么属性,就监听什么属性。没有b,当然就不会监听b了。给obj.b赋值的操作,Vue当然也就不会知道了。不知道当然就没办法响应了。

这个就是bug。这个bug是由Object.defineProperty带来的。因为使用它就必须有一个属性,比如n。所以,如果我一开始没定义某个属性,该属性就不会被监听,那我之后要改变它的时候,就无法更新了。

解决方法:

  1. 我一开始定义一个b不就好了,只是不给值而已
    data: {
        obj: {
          a: 0 ,// obj.a 会被 Vue 监听 & 代理
          b:undefined
        }
    },
    
    这样点击按钮,会显示1.

但是,有些人也不会提前写,就导致之后在赋值的时候说页面没更新。

  1. 使用Vue.set 或 this.$set

    setB() {
      Vue.set(this.obj, "b", 1);
    }
    

    如果想赋值,发现一开始没有定义这个属性。就可以使用Vue.set()。

    Vue.set做了什么?

    首先,当然是在data.obj里新增了一个属性b,值是1.

    然后,自动为obj.b创建监听和代理。就像Object.defineProperty做的一样

    触发UI更新

    之后如果再想改变b的值,因为现在已经监听并且代理了,所以之后直接this.obj.b就可以改变了,并且Vue也能知道,然后渲染到页面中。

data经过了Vue之后,最重要的是监听和代理。所以在实例已经创建的情况下,再为实例添加一个属性不只是单纯赋值那么简单,还要监听和代理,这样Vue才能知道数据的改变并且渲染。

在这里我把obj那层对象删掉了,直接在data下边使用Vue.set。发现报错了。

查了资料后知道, Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property)。然而它可以使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上。所以obj是必须的。