仿写Vue七、响应式

七、响应式

自己动手实践,就会更加深刻的理解

前面的几篇文章,大致解决了从模板与数据到虚拟节点,再到渲染至页面上这一过程,接下来的几篇,着手解决响应式,即数据发生修改,页面随即更新的功能。

01、数据劫持


我们都知道在 Vue 2.x 版本中,数据的双向绑定是靠 Object.defineProperty()来实现的。
在 3 的版本中使用的 Proxy来代理。毫无疑问,proxy实现起来更简单。本着学习的目的,我们还是使用前者来学习实现吧。至于后者,以后有时间总会尝试的!

  1. 公共函数:判断是否为对象
function isObject(obj) {  
  return obj !== null && typeof obj === 'object';
}
  1. 声明 Observer 构造函数,此函数将普通对象转换为响应式对象
/** * 对与响应式数组,我们需要重写其一些方法,例如 push、pop * 保证响应式数组使用这些方法之后依然是响应式的。 */
const arrProto = Array.prototype;
const arrMethods = Object.create(arrProto);

function Observer(value) {  
  this.value = value;  def(value, '__ob__', this); // 用来标志响应式  
  if (Array.isArray(value)) {    
    value.__proto__ = arrMethods;    
    this.observeArray(value);  
  } else {    
    this.walk(value);  
  }
}
  1. 在 Observer 原型上定义两个方法,如果是数组,就调用 observeArray,如果是普通对象,就调用 walk
/**
 * Walk through all properties and convert them into
 * getter/setters. This method should only be called when
 * value type is Object.
 */
Observer.prototype.walk = function (obj) {
  for (const key in obj) {
    defineReactive(obj, key, obj[key]);
  }
}

/**
 * Observe a list of Array items.
 */
Observer.prototype.observeArray = function (items) {
  for (const item of items) {
    observe(item);
  }
}

02、普通对象defineReactive


对于一个对象,遍历其键值对,并对每个键值对调用 defineReactive 方法:

/**
 * Define a reactive property on an Object.
 * 这里使用到了闭包
 */
function defineReactive(obj, key, val) {
  observe(val);

  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: true,
    get: function reactiveGetter() {
      console.log(`get ${key}: ${val}`);
      return val
    },
    set: function reactiveSetter(newValue) {
      console.log(`set ${key} from ${val} to ${newValue}`);
      val = newValue;
      observe(val); // 对于直接给数组复制的情况需要添加响应式
    },
  })
}

03、observe 函数


observe:将数据转换为响应式的入口函数,判断是否已经为响应式,若不是,则新创建一个 Observer 对象,如果是,则直接返回响应式。

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
function observe(obj) {
  if (!isObject(obj)) {
    return
  }
  if (obj.hasOwnProperty('__ob__')) { 
    // 如果已经是响应式
    return obj.__ob__;
  } else {
    return new Observer(obj);
  }
}

04、效果图


原始数据:

    const data = {
      name: 'romeo',
      message: 'wants to be rich',
      deep: {
        firstLevel: {
          secondLevel: 'here!'
        }
      },
      arr: [
        { name: 'jack1' },
        { name: 'jack2' },
        { name: 'jack3' },
      ]
    }
  1. getter
    仿写Vue七、响应式_第1张图片
  2. setter
    仿写Vue七、响应式_第2张图片
  3. 新的赋值是否仍然为响应式
    仿写Vue七、响应式_第3张图片

下期预告:添加数组的一系列方法~

源代码在github上,请点击阅读原文查看~
仿写Vue七、响应式_第4张图片

你可能感兴趣的:(vue)