Vue-数据响应式

216 阅读4分钟

Vue的Data

ES 6的setter和getter

对象属性也可以是一个函数、gettersetter方法。 语法

var o = {
  property: function ([parameters]) {},
  get property() {},
  set property(value) {},
};

Object.defineProperty

作用:

  • 可以给对象添加属性value
  • 可以给对象添加getter/setter, getter/setter用来对属性的读写进行监控

语法:

var o = {}; // 创建一个新对象

// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

代理模式

代理的设计模式,意思为vm实例负责对myData对象属性的读写。即vm就是myData的代理,类似于房屋中介,住户通过中介和房东交流。例如通常vm.n来操作myData.n,而不是直接操作myData.n

原理

const vm = new Vue({data: myData}) 作用:

  • 核心目的:对于data的任何变动,都得知情,从而重新进行渲染
  • 会让vm成为myData的代理,即proxy
  • 同时会对myData的所有属性进行监控
  • 监控的原因是,为了防止myData属性改变的时候,vm不知情。如果通知vm了,就可以进行操作,然后调用render(data)方法,渲染视图,即UI=render(data)
  • 全程对data进行修改,并没有复制和生成新的data,只有覆盖修改
  • 如果data有多个属性,n,m,k,那么就会有get n/get m /get k

实例

下面的例子是类似原理,已经很完善了,但是不通过设置vm,通过设置另外一个引用,可以直接修改data的值。

let data3 = proxy({data:{n:0}})

function proxy({data}){ // 解构赋值
  const obj = {}
  // 这里的n进行了简化,理论上需要遍历data的所有key
  Object.defineProperty(obj, 'n', {
   get(){
       return data.n
   },
   set(value){
       if(value<0) return
       data.n = value
   }
  })
  return obj  // obj就是代理
 }

这是最终的类似原理实现,实现了对data的监听和代理

let myData5 = {n: 0}
let data5 = proxy2({myData5})

function proxy2({data}){ // 解构赋值
  let value = data.n
  delete data.n     //这行可以省略,下面的代码会覆盖data.n
  
  // 这里的n进行了简化,理论上需要遍历data的所有key
  Object.defineProperty(data, 'n', {
   get(){
       return value
   },
   set(newValue){
       if(value<0) return
       value = newValue 
   }
  }) // 这里是监听逻辑
  
  Object.defineProperty(obj, 'n', {
   get(){
       return data.n
   },
   set(value){
       data.n = value
   }
  })
  return obj // obj就是代理
 }
 
 const vm = new Vue({data:{n:0}) // 等价于let data5 = proxy2({myData5})

数据响应式

数据响应式是指当数据改变时,UI或者视图能够做出相应的反应。

data属于响应式

const vm = new Vue({data:{n:0}})中,如果修改vm.n,那么UI中的n就会做出响应

Vue的数据响应式

Vue响应式系统即对数据进行修改时,视图会进行更新; 当把JS对象传入Vue实例作为 data 选项,Vue将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/settergetter/setter 对用户不可见,但是在内部它们让 Vue 能够追踪依赖,在属性被访问和修改时通知变更。

响应式网页

响应式网页是指当用户改变窗口大小,网页内容会做出响应

Vue的Bug

  • Object.defineProperty有问题

Object.definePropery(obj, 'n', {...})时,必须要有一个'n',才能监听和代理obj.n。如果没有设置n,Vue会给出一个警告或者只会检查第一层属性 ,此时如果使用set方法,不会奏效,因为Vue没有办法监听一开始就不存在的属性。页面会显示空白

  • 解决办法
  1. 事先声明key,然后不再添加属性
  2. 使用vue.set或者this.$set

对象中新增的key

Vue没有办法事先监听和代理,需要使用set来新增key,创建监听和代理,更新UI。推荐提前把属性都写好,不要新增key

Vue.set和this.$set

作用:

  • 新增key,即对象的键名或者属性名
  • 如果没有创建过,自动创建代理和监听
  • 触发UI更新,但不会立刻更新,有延迟 实例:
methods:{
    add({
        vue.set(this.obj, 'b', 1)
        // 或者
        this.$set(this.obj, 'b', 1)
    }
}

数组中新增的key

由于数组的长度可以一直增加,没有办法提前把数组的key都声明出来,Vue也不能检测新增的下标。可以使用set来新增key, 但不会自动添加监听和代理,然后更新UI。 但是尤雨溪纂改了7API来对数组进行操作,这7API会自动处理监听和代理,并更新UI

推荐使用这7API来新增数组key:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

使用代码模拟类似原理

class VueArray extends Array {
  push(...args) {
    const oldLength = this.length; // this
    super.push(...args);
    console.log(" push ");
    for (let i = oldLength; i < this.length; i++) {
      Vue.set(this, i, this[i]);
      // key Vue
    }
  }
}

更多信息

Vue 深入响应式原理

ES 6更新特性

MDN 对象初始化

MDN Object.defineProperty()

Vue 变异方法 (mutation method)

《Vue 自测题》中答错率最高的题的解释