Vue 响应原理(reactivity)

2020-02-22  本文已影响0人  Alaricming

1. getter / setter

Object.defineProperty
var obj = {};
obj.defineProperty(obj, prop, descriptor);

// descriptor 可选属性包括:
// enumerable: bool    (数据/存储描述符)定义该属性能否被for..in,Object.keys 等遍历出来
// configurable: bool  (数据/存储描述符)定义该属性是否可以改变,能否从对象上被删除
// value: any          (数据描述符)该属性对应的值
// writable: bool      (数据描述符)仅当writable 为 true 时,value 才能被改变
// get: func            (存取描述符) 访问该属性时,getter 方法会被执行,默认为 undefined    
// set: func            (存取描述符) 当属性发生修改时会触发该方法,接受新的值作为唯一参数

需要注意的是:

2. dependency tracking

当把一个普通的js对象传入Vue 实例作为data选项,Vue 将遍历此对象的所有属性,并利用 Object.defineProperty 将这些属性全部变为getter/setter,从而追踪属性的访问和修改,每个组件都有一个watcher,他会把组件渲染过程中"接触"到的数据记录为依赖,当依赖项的setter触发时,通知watcher,从而使它关联的组件重新渲染。

function convert(obj) {
  Object.keys.forEach(ele => {
    let initialValue = Object[key];
    
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get(){
        console.log(`getting ${key} value: ${initialValue}`);
        return initialValue
      },
      set(newVal) {
        console.log(`assign ${newVal} to ${key}`);
        initialValue = newVal
      }
    })
  })
}

上面是一个简单的转换功能。

实际上,需要一个完整的依赖追踪需要的做的事比较多:

代码如下:

const dep = new Dep();

autorun(() => {
  dep.depend();
  console.log('updated')
});

// 任何时候调用,都会触发 autorun 接受的函数参数
dep.notify();


现在我们需要实现的是 Depautorun

window.Dep = class Dep {
  constructor() {
    this.subscribers = new Set();
  }
  
  depend() {
    if(activeUpdate) {
      // register the current active update as a subscriber
      this.subscribers.add(activeUpdate);
    }
  }
  
  notify() {
    // run all subscriber functions
    this.subscribers.forEach(sub = sub());
  }
}

// 声明一个 `activeUpdate`, 方便在外部可以访问
let activeUpdate;

function autorun(update) {
  function wrappedUpdate() {
    activeUpdate = wrappedUpdate;
    update();
    activeUpdate = null;
  }
}

autorun(() => {
  dep.depend()
})

3. observer

一个简单的observer 其实就是把上述两者合并。

window.Dep = class Dep(){
  constructor(){
    this.subscribers = new Set();
  }
  // 添加依赖
  depend() {
    if(activeUpdate) {
      this.subscribers.add(actievUpdate);
    }
  }
  // 添加订阅
  notify() {
    this.subscribers.forEach(sub => sub())
  }
}

// dealare a variable to expose inner update function
let activeUpdate; 
function antorun(update) {
  function wrappedUpdate() {
    activeUpdate = wrappedUpdate;
    update();
    activeUpdate = null;
  }
}

function convert(state){
  const dep = new Dep();
  
  Object.keys(state).forEach(ele => {
    let initialValue = state[ele];
    
    Object.defineProperty(state, ele, {
      enumberable: true,
      configurable: true,
      get(){
        dep.depend();
        return initialValue;
      },
      set(newValue) {
        dep.notify();
        initialValue = newValue;
      }
    })
  })
}

关于 Vue 响应原理,也可以参考这篇文章:
Build a Reactivity System

更多资源:
尤雨溪教你写vue 高级vue教程 源码分析

上一篇 下一篇

猜你喜欢

热点阅读