Vue 2.x中的响应式实现原理-1

221 阅读2分钟

这是我参与8月更文挑战的第26天,活动详情查看: 8月更文挑战

前言

MVVM模式最核心的特性就是数据双向绑定,Vue构建了一套响应式系统,可以实现用声明的方式绑定数据,从而在数据发生变化时自动渲染视图

Vue 2.x 响应式系统的实现原理

Vue 2.x是利用 Object.defineProperty() 方法为对象添加get() 和 set() 方法来侦测对象的变化,当获取对象属性值时会调用get() 方法,当修改对象属性值时会调用set()方法,于是可以在get()和set()方法中添加代码,实现数据与视图的双向绑定。代码示例如下:

// 对Object.defineProperty() 方法进行封装
function defineReactive(obj, key, value){
  Object.defineProperty(obj, key, {
    get() {
      return value
    },
    set(newValue) {
       if(newValue !== value) {
         updateView(); // 在set()方法中触发更新
         value = newValue
       }
    }
}

// 对一个对象中所有属性的变化进行侦测
function observer(target){
  // 如果不是对象数据类型,则直接返回
  if(typeof target !== 'object' ) {
    return target
  }  
  // 循环遍历对象的所有属性,并将他们转化为getter和setter形式
  for(let key in target){
    difineReactive(target, key, target[key])
  }
}

// 模拟更新视图的方法
function updateView() {
  console.log("视图更新")
}

let user = {name: 'jack'}
// 对user对象的所有属性变化进行侦测
observer(user)
user.name = 'tom'

运行上述代码,输出结果:

视图更新

以上代码只是简单的侦测了一个对象的属性变化,并没有考虑到对象属性本身又是一个对象的情形(即对象的嵌套)。假设user对象中有一个属性address,该属性本身也是一个对象。代码如下:

let user = {name: 'jack', address: {city: '北京'}
// 对user对象的所有属性变化进行侦测
observer(user)
user.address.city = '河北'

运行上述代码,将看不到任何输出,说明对address对象的city属性的修改并没有被侦测到。因此,需要修改上述的代码,当对象的属性也是对象类型时,继续为该属性对象的所有属性添加get() 和 sett() 方法。实现起来也很简单,就是在defineReactive() 函数中添加一个observer() 函数的递归调用。代码示例如下:

// 对Object.defineProperty() 方法进行封装
function defineReactive(obj, key, value){
  // 通过递归调用解决多层对象嵌套的属性侦测问题
  observer(value)
  Object.defineProperty(obj, key, {
    get() {
      return value
    },
    set(newValue){
      if(newValue !== value) {
        //observer(newValue)
         updateView(); // 在set()方法中触发更新
         value = newValue
       }
    } 
  })
}

再次运行代码,可以看到 更新视图 的输出