KevinHu-1024/kevins-blog

从 Timeline 探索 Vue2 源码(二)——响应式改造

KevinHu-1024 opened this issue · 0 comments

未完待续,pending...

书接上回 #1

响应式要达到的几种效果

  1. 拦截扁平对象键值的get/set
var a = {
  name: 'kevin',
  age: 0,
};

for(let key in a) {
  if(a.hasOwnProperty(key)) {
    reactive(a, key, a[key]); // 对每一个键,生成一个闭包,把键值传入
  }
}

function reactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get: function() {
      console.log(`访问${key}`);
      return val;  // get的时候访问闭包中的val
    },
    set: function(newVal) {
      console.log(`写入${key}:${newVal}`);
      val = newVal;  // set的时候修改闭包中的val,实际上a中的属性根本就没改,只是set被我们拦截了下来,改了get要返回的闭包中的值而已
    }
  });
}

// 测试
  1. 对于嵌套的对象,能够保证上一条效果
var a = {
  name: 'kevin',
  age: 0,
  location: {
    province: 'bj',
    detail: {
      district: 'haidian',
    }
  }
};

function walk(obj) {
  const isObject = (value) => Object.prototype.toString.call(value) === '[object Object]';

  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      var item = obj[key];
      if (isObject(item)) {
        arguments.callee(item);
      } else {
        reactive(obj, key, item); // 对每一个不是对象的键,生成一个闭包,把键值传入
      }
    }
  }
}

function reactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get: function() {
      console.log(`访问${key}`);
      return val;  // get的时候访问闭包中的val
    },
    set: function(newVal) {
      console.log(`写入${key}:${newVal}`);
      val = newVal;  // set的时候修改闭包中的val
    }
  });
}

walk(a);

// 测试
  1. 按照源码的风格封装了一下:
var a = {
  name: 'kevin',
  age: 0,
  location: {
    province: 'bj',
    detail: {
      district: 'haidian',
    }
  }
};

class Observer {
  constructor(data) {
    this.data = data;

    if (Observer.isObject(data)) {
      this.walk(data);
    } else {
      console.error(`${Object.prototype.toString.call(data)} 不是对象`);
    }
  }

  static isObject(value) {
    return Object.prototype.toString.call(value) === '[object Object]'
  }

  walk(obj) {
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        var item = obj[key];
        if (Observer.isObject(item)) {
          new Observer(item);
        } else {
          this.reactive(obj, key, item); // 对每一个不是对象的键,生成一个闭包,闭包不会被销毁
        }
      }
    }
  }

  reactive(obj, key, val) {
    Object.defineProperty(obj, key, {
      get: function() {
        console.log(`访问${key}`);
        return val;  // get的时候访问闭包中的val
      },
      set: function(newVal) {
        console.log(`写入${key}:${newVal}`);
        val = newVal;  // set的时候修改闭包中的val
      }
    });
  }
}

new Observer(a);

reactive-ppt

响应式的目标

  1. 改造一个对象 / 数组为响应式
  2. 当我修改对象键值 / 数组元素时,能够通知外界被更改的项及新值
  3. 当我访问对象键值 / 数组元素时,能够通知外界被更改的项及新值

以下面的对象为例:

var a = {
  name: 'kevin',
  age: 0,
  location: {
    province: 'bj',
    detail: {
      district: 'haidian',
    }
  }
};