vue响应式原理

vue作为一个MVVM框架,是如果对数据属性实现响应式的呢?通过深入研究,发现它是通过Object.defineProperty(只支持纯对象)绑定get,set来实现的,下面就来探究一下其中的原理。

Object.definePropety()

/**
 * @param {[Object]} obj 目标对象
 * @param {[String]} prop 目标对象的属性
 * @param {[String]} descriptor [属性描述符:数据描述符和存取描述符两种形式]
 */
var obj = {};
var descriptor = Object.create(null) // 没有继承的属性
// 默认没有enumerable,没有configurabel,没有writeable
descriptor.value  = 'static';
object.definePorperty(obj, 'key', descriptor);

// 属性描述符
object.definePorperty(obj, 'key', {
  enumerable: false,  // 定义对象的属性是否是可枚举
  writeable: faslse,  // 定义对象的属性是否可修改
  configurable: fase, // 定义对象属性是否可删除
  value: "static"
})

// 存取描述符
var bValue
object.defineProperty(obj, 'key', {
  get: function() {
    return bValue;
  },
  set: function(newValue) {
    bValue = newValue
  },
  eumerable: true,
  configurable: true
})

vue在denfineProperty的方法上进行进一步的封装

function defineReactive(data,key,value) {
  Object.defineProerty(data,key,{
    get: function() {
      return value
    },
    set: function(newValue) {
      if(newValue === value) return;
      value = newValue;
    },
    eumerable: true,
    writeable: true
  })
}

怎么观察Observer

我们之所以要观察Observer,是为了当数据属性发生变化的时候,通知那些使用到该属性的地方。
这要我们就可以先把所有使用到该属性依赖全部收集起来,当属性改变的时候,循环通知所有的属性依赖进行更新

依赖搜集

在defineReative的基础上进一步实现

/**
 * [defineReactive description]
 * @param  {[Object]} data  [对象obj]
 * @param  {[String]} key   [对象属性]
 * @param  {[String]} value [对象属性的值]
 * @return {[type]}       [description]
 */
function defineReactive(data,key,value) {
  let dep = [];
  Object.defineProperty(data,key,{
    eumerable: true,
    configurable: true,
    get: function() {
      dep.push(watcher);
    },
    set: function(newValue) {
      if(value === newValue) return;
      for(let i = 0; i < dep.length; i++ ) {
        watcher.update(newValue, value);
      }
      value = newValue
    }
  })
}


// 进一步把收集依赖的部分封装起来
class Dep {
  static target: ?Watcher;
  id: Number;
  subs: String<watcher>;

  construstor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub(sub: Watcher) {
    remove(this.subs, sub)
  }

  depend() {
    if(Dep.target == Watcher) {
      this.addSub(Dep.target)
    }
  }

  notify() {
    const subs = this.subs.splice();
    for(let i = 0; i < subs.length; i++) {
      subs[i].update();
    }
  }
}

// 修改后的代码
function defineReactive(data,key,value) {
  let dep = New Dep();
  Object.defineProperty(data,key, {
    eumerable: true,
    configaruable: true,
    get: function() {
      dep.depend();
      return value;
    },
    set: function(NewValue) {
      if(value === NewValue) return;
      dep.notify();
      value = NewValue;
    }
  })
}

总结: 要观察就要收集依赖到Dep用于专门存储依赖, 上面我们将所有存储到dep数组的叫做watcher。致这些watcher依赖是在vue中的templete,watch,computed中都要是收集的,当数据改变的时候就去通知到这些地方,这样就需要抽象出一个能够处理不同情况的类,然后我们在依赖收集的时候只需要收集这些封装好的类进来,通知也是通知到它一个,然后它负责通知其他地方使用到该属性的地方,这个类我们叫它watcher

watcher

class Watcher {
  constructor(expOrFn, callback) {
    this.getter = parsePath(expOrFn);
    this.callback = callback;
    this.value = this.get();
  }

  get() {   // 执行get, 将自己注入到dep.target
    Dep.target = this;
    this.getter.call(vm);   // 触发getter,getter里面触发depend, depend执行addSub,将watcher放到dep依赖中
    Dep.tabget = undefined;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.callback(this.value, oldValue)
  }
}

上面的代码就是一个完整的一个watcher,所做的事情就是获取和更新要用到的属性

至此,单个属性监测响应的整个过程就实现了,对于多个属性的监测代码实现如下,遍历对象的所有属性

function observe(obj) {
  if(obj.constructor !== Object) return;
  const keys = obj.keys();
  for(let i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i], obj[keys[i]])
  }
}

function defineReactive(data,key,value) {
  observe(val);
  let dep = new Dep();
  Object.defineProperty(data,key, {
    eumerable: true,
    configurable: true,
    get: function() {
      dep.depend();
      return value;
    },
    set: function(newValue) {
      if(value === newValue) return;
      dep.notify();
      value = newValue;
    }
  })
}

说明

  • 属性的可枚举性: 可以通过for-in循环来进行遍历
  • Object.keys() : 该方法会返回一个由一个给定对象的自身可枚举属性组成的数组
原文地址:https://www.cnblogs.com/Imflyer/p/8080728.html