美文网首页
Vue 响应原理(reactivity)

Vue 响应原理(reactivity)

作者: Alaricming | 来源:发表于2020-02-22 11:04 被阅读0次

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            (存取描述符) 当属性发生修改时会触发该方法,接受新的值作为唯一参数

需要注意的是:

  • 数据描述符存取描述符 不能同时出现,只能是两种形式中的一个,即:value writableget set不能一起用
  • configurable 一旦定义便不可改变,否则报错
  • configurablewritable 作用相似,当同时设置时以writable 为主,也就是说:{ configurable: false, writable: true 时,属性值仍可操作

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
      }
    })
  })
}

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

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

  • 创建一个 Dep 类, 包含两个方法:dependnotify
  • 创建一个 autorun 方法,接受一个 update 函数作为参数
  • update 方法内部,可以通过实例化 Dep 的实例dep,并调用dep.depend 来生成依赖
  • 然后,通过调用 dep.notify 来出发触发 update

代码如下:

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教程 源码分析

相关文章

网友评论

      本文标题:Vue 响应原理(reactivity)

      本文链接:https://www.haomeiwen.com/subject/cmxlqhtx.html