Vue 数据响应式

157 阅读2分钟

什么是数据响应式?

Vue.js文档 中这样说: Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接。

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。


Object.defineProperty() 的用法:

let data2 = {}

data2._n = 0 // _n 用来偷偷存储 n 的值

Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n = value
  }
})

Object.defineProperty() 可以给对象添加属性value,可以给对象添加 getter/setter, getter/setter 用于对属性的读写进行监控。

但是这里有一个问题是,我们可以通过修改变量 data2._n 来改变里面的值,为了解决这个问题,需要使用代理:

let data3 = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问

function proxy({data} /* 解构赋值*/){
  const obj = {}
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  return obj // obj 就是代理
}

但上面的代码还可以通过下面的方式来修改:

let myData = {n:0}
let data4 = proxy({ data:myData })

为了解决这样的问题,还需要添加监听:

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

function proxy2({data}/* 解构赋值 */){
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  
  return obj // obj 就是代理
}

以上大概就是 Vue 实现数据响应式的原理。

vm = new Vue({data:myData})

一、会让 vm 成为 myData 的代理(proxy)

二、会对 myData 的所有属性进行监控

当 vm 监控到 myData 的属性发生变化,就可以调用 render(data) 来更新视图。