美文网首页
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