Skip to content

API 回顾 Object.defineProperty

js
var obj = {
  id: 101,
  name: "tom",
};

// 定义getter/setter
function defineGetterSetter(obj, key) {
  var val = obj[key];
  Object.defineProperty(obj, {
    configurable: false,
    enumerable: true,
    get() {
      // 获取值
      console.log("## getter:", val);
      return val;
    },
    set(newVal) {
      // 设置值
      console.log("## setter:", newVal);
      val = newVal;
    },
  });
}

发布订阅模式

js
// 发布者
class Publisher {
  constructor() {
    this.subs = [];
  }

  // 添加订阅者
  addSubscriber(sub) {
    this.subs.push(sub);
  }

  // 通知所有定订阅者: 执行 update 方法
  // 反过来说, 订阅者必须有 update 方法
  notify(){
    this.subs.forEach(item => item.update());
  }
}

// 订阅者
class Subscriber{
  constructor() {
    // 第一次执行做一些操作
  }

  update() {
    // Publisher 的通知后,执行更新操作
  }
}

vue 响应式实现原理流程

  1. new Vue 时, 用 Object.defineProperty 给 data 中所有的数据定义 getter/setter
  2. 利用发布/订阅模式, 在 getter 中收集依赖, 在 setter 通知依赖的订阅者更新, 走到setter中证明数据更新了

reactive

vue 如何监听数组变化

有些改变数组本身的方法无法被 Object.defineProperty 监听到: sort,reverse,push,pop,shift,unshift,splice

  • 重写数组上的改变数组本身的方法, 类似代理模式, 在自己实现的方法中收集依赖
js
// 代理数组方法: 因为这些方法会改变原数组,
// 而且无法被 Object.defineProperty 监听到
function proxyArrayMethods(arr) {
  const methods = ["pop", "push", "shift", "unshift", "splice", "sort", "reverse"];
  const arrayProto = Array.prototype;
  const middleman = Object.create(arrayProto);

  for (const m of methods) {
    const original = arrayProto[m];
    middleman[m] = function proxyMethod(...args) {
      // 执行原来的数组方法
      const result = original.apply(this, args);

      // 如果调用的新增元素的方法, 需要获取新增的值, 然后观察它
      // 让新增的这个元素也具备响应式的功能
      let insertedElements;
      switch (m) {
        case "push":
        case "unshift":
          insertedElements = args;
          break;
        case "splice":
          insertedElements = args.slice(2);
          break;
      }
      insertedElements && observe(insertedElements);
      return result;
    };
  }

  // 给数组设置原型
  Object.setPrototypeOf(arr, middleman);
  return arr;
}

源码

https://github.com/liaohui5/mvvm-source-demo

Released under the MIT License.