Skip to content

两个版本响应式

Vue2

监听对象

通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)

  • 对象中后追加的属性,Vue 默认不做响应式处理

  • 如需给后添加的属性做响应式,请使用如下 API:

    • Vue.set(target,propertyName/index,value)
    • vm.$set(target,propertyName/index,value)
js
let data = {
  name: "Tom",
  age: 18,
  score: {
    math: 12,
    english: 20,
  },
};
//创建一个监视的实例对象,用于监视data中属性的变化
let obs = new Observer(data);
//准备一个vm实例对象
let vm = {};
vm._data = obs;

function Observer(obj) {
  let keys = Object.keys(obj);
  for (let k of keys) {
    Object.defineProperty(this, k, {
      get() {
        return obj[k];
      },
      set(value) {
        obj[k] = value;
      },
    });
    if (typeof obj[k] === "object" && obj[k] !== null) {
      obj[k] = new Observer(obj[k]);
    }
  }
}

监听数组

通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)

在 Vue 修改数组中的某个元素一定要用如下方法

  1. push()pop()shift()unshift()splice()sort()reverse()
  2. Vue.set()vm.$set()

注意

vue 会监视 data 中所有层次的数据;
Vue.set()vm.$set() 不能给 vm 或 vm 的根数据对象 添加属性

Vue3

Proxy(代理)

通过Proxy拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等

js
let data = {
  name: "Tom",
  age: 49,
};
let p = new Proxy(data, {
  get(target, propName) {
    return target[propName];
  },
  // 修改和新增
  set(target, propName, value) {
    target[propName] = value;
  },
  deleteProperty(target, propName) {
    //deleteProperty必须返回一个布尔值表示是否删除成功
    return delete target[propName]; // delete执行返回true
  },
});

Reflect(反射)

通过 Reflect(反射): 对源对象的属性进行操作

js
let data = {
  name: "Tom",
  age: 49,
};
let p = new Proxy(data, {
  get(target, propName) {
    return Reflect.get(target, propName);
  },
  // 修改和新增
  set(target, propName, value) {
    Reflect.set(target, propName, value);
  },
  deleteProperty(target, propName) {
    //deleteProperty必须返回一个布尔值表示是否删除成功
    return Reflect.deleteProperty(target, propName);
  },
});

reactive 简易实现

js
// 判断是否是对象
const isObject = (val) => val !== null && typeof val === "object";

// 数据
let data = {
  name: "Tom",
  age: 49,
  score: {
    math: 20,
    art: 39,
  },
};

function reactive(target) {
  if (!isObject(target)) return target;
  const handler = {
    get(target, propName) {
      console.log(`获取对象属性${propName}值`);
      const result = Reflect.get(target, propName);
      if (isObject(result)) {
        // 值如果是引用类型, 递归为其每个内部元素添加代理
        return reactive(result);
      }
      return result;
    },
    set(target, propName, value) {
      console.log(`设置对象属性${propName}值`);
      return Reflect.set(target, propName, value);
    },
    deleteProperty(target, propName) {
      console.log(`删除对象属性${propName}值`);
      return Reflect.deleteProperty(target, propName);
    },
  };
  return new Proxy(target, handler);
}

const r = reactive(data);