Proxy-Reflect 响应式原理 vue3/vue2

总结: 
1. 什么是响应式
2. 响应式函数的封装
3. Depend 类的封装
4. 监听对象的变化
      a. new Proxy(, set: depend.notify)
5. 依赖收集的数据结构 weakMap
6. 正确的收集依赖
      a. Proxy 的get方法中收集对应的函数
      b. 在全局activeReactiveFn变量
      c.get中找到depend对象,addDepend(全局activeReactiveFn变量)
7. 对Depend进行优化
      a. addDepend函数换成depend函数
      b. 将保存的数组换成set
8. 对对象的响应式操作
      a. new Proxy() vue3
      b. Object.defineProperty() vue2

一、Proxy

1、监听对象的操作 - Object.defineProperty()

通过使用Object.defineProperty()

const obj = {
  name: "chen",
  age: 23,
};
// 1.监听一个属性
// Object.defineProperty(obj, "name", {
//   get: function () {
//     console.log("监听到name被访问");
//     return this;
//   },
//   set: function () {
//     console.log("监听到被设置");
//   },
// });
// console.log(obj.name);
// obj.name = '天选之子'

// 2.监听两个属性
Object.keys(obj).forEach((item) => {
  console.log(item);
  Object.defineProperty(obj, item, {
    get() {
      console.log("get监听");
    },
    set(value) {
      obj['item'] = value;
      console.log("set设置");
    },
  });
});
obj.name = "kobe";
obj.age = 23;
console.log(obj);

弊端: 在这里插入图片描述

2、监听对象的操作2 - 引出 Proxy

在这里插入图片描述

const obj = {
  name: "chen",
  age: 23,
};

// 参数: 对象, 捕获器对象
const objProxy = new Proxy(obj, {
  // 1、获取值时的捕获器, 会替换调原来获取值的,自动回调get
  // target:就是被代理的对象  key:obj中的key
  get(target, key) {
    return target[key];
  },
  // 2、设置值时的捕获器
  set(target, key, value) {
    target[key] = value;
  },

  // 3、监听in捕获器
  has(target, key){
    console.log('监听in操作');
    return key in target;
  },
  // 4、监听delete操作
  delete(target, key){
    console.log('监听删除操作');
    delete target[key];
  }
});
objProxy.name = "陈胜";
console.log(objProxy.age); //23
console.log(objProxy.name); //chen

// 发现原来的obj对象也被改掉了
console.log(obj.age); //23
console.log(obj.name); //chen

// in操作符
console.log("name" in objProxy);//true

// delete操作
delete objProxy.name;
console.log(obj);

在这里插入图片描述

3、Proxy对函数对象的监听
function foo() {}

const fooProxy = new Proxy(foo, {
  apply: function (target, thisArg, argArray) {
    console.log("对apply进行调用");
    return target.apply(thisArg, argArray);
  },
  construct: function (target, argArray) {
    console.log("对new进行调用");
    return new target(...argArray);
  },
});
foo();

fooProxy.apply({}, ["abc", "cde"]); // apply进行调用
new fooProxy("abc", "cdd"); // new进行调用

二、Reflect

1、Reflect 引入

在这里插入图片描述
在这里插入图片描述
刚好跟Proxy中的13个方法一一对应

const obj = {
  name: "chen",
  age: 23,
};

const objProxy = new Proxy(obj, {
  get(target, key) {
    // return target[key];
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    // target[key] = value;
    // 其实Reflect设置值会返回一个布尔值的
    Reflect.set(target, key, value);
  },
});
objProxy.name = "陈胜";
console.log(objProxy.age); //23
console.log(objProxy.name); // 陈胜

console.log(obj.age); //23
console.log(obj.name); //陈胜
2、Reflect 的作用
const obj = {
  _name: "chen",
  get name() {
    return this._name; //不好确定this,
  },
  set name(value) {
    this._name = value;
  },
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    // receiver是创建出来的代理对象objProxy,
    console.log("get方法被访问---", key, receiver);
    console.log(receiver === objProxy);
    // Reflect第三个参数,如果传了,那么就会被传递到的obj调用get name()方法对象里面
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log("set方法被访问---", key, receiver);
    Reflect.set(target, key, value, receiver);
  },
});
console.log(objProxy.name);
objProxy.name = "天选之子";
// 注:set、get都会别访问两次
3、Reflect中的construct作用
function Student(name, age) {
  // 我们之前是调用super(name, age)
  this.name = name;
  this.age = age;
}

function Teacher() {}

// 执行student里面的内容,创建出来的对象是Teacher类型
const teacher = Reflect.construct(Student, ["chen", 23], Teacher);
console.log(teacher); //Teacher { name: 'chen', age: 23 }
console.log(teacher.__proto__ === Teacher.prototype); //true

三、响应式原理

1、响应式函数的封装
// 封装一个响应式函数
// name发生改变的所有需要重新执行的函数, 数组不是很方便,如果里面有age呢, 我们该用类
let reactiveFns = [];
function watchFn(fn) {
  reactiveFns.push(fn);
}

// 对象的响应式
const obj = {
  name: "chen",
  age: 23,
};
watchFn(function () {
  const newName = obj.name;
  console.log("你好");
  console.log(obj.name);
});

watchFn(function () {
  console.log(obj.name, "demo function----");
});

obj.name = "天线大爷";
reactiveFns.forEach((fn) => {
  fn();
});
2、依赖收集类的封装
class Depend {
  constructor() {
    this.reactiveFns = [];
  }
  // 让每个属性对应一个Depend类
  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn);
  }
  notify() {
    this.reactiveFns.forEach((fn) => fn());
  }
}

// 封装一个响应式函数
// name发生改变的所有需要重新执行的函数, 数组不是很方便,如果里面有age呢, 我们该用类
// let reactiveFns = [];
const depend = new Depend();
function watchFn(fn) {
  // reactiveFns.push(fn);
  depend.addDepend(fn);
}

// 对象的响应式
const obj = {
  name: "chen",
  age: 23,
};
watchFn(function () {
  const newName = obj.name;
  console.log("你好");
  console.log(obj.name);
});

watchFn(function () {
  console.log(obj.name, "demo function----");
});

obj.name = "天线大爷";
depend.notify();
// reactiveFns.forEach((fn) => {
//   fn();
// });
3、自动监听对象变化
class Depend {
  constructor() {
    this.reactiveFns = [];
  }
  // 让每个属性对应一个Depend类
  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn);
  }
  notify() {
    this.reactiveFns.forEach((fn) => fn());
  }
}

// 封装一个响应式函数
// name发生改变的所有需要重新执行的函数, 数组不是很方便,如果里面有age呢, 我们该用类
// let reactiveFns = [];
const depend = new Depend();
function watchFn(fn) {
  // reactiveFns.push(fn);
  depend.addDepend(fn);
}

// 对象的响应式
const obj = {
  name: "chen",
  age: 23,
};

// 监听对象的属性变化:
// 两种方式: 1、Proxy(vue3实现原理)  2、Object.defineProperty(value, property, receiver)
const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    // 改变属性自动执行notify
    depend.notify();
  },
});

watchFn(function () {
  const newName = objProxy.name;
  console.log("你好");
  console.log(objProxy.name);
});

watchFn(function () {
  console.log(objProxy.name, "demo function----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化1----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化2----");
});

// 监听对象变化,执行三次,响应式
objProxy.name = "天线大爷";
objProxy.name = "天线大爷2";
objProxy.name = "天线大爷3";
objProxy.age = 200;

// 不应该手动,应该监听对象属性变化
// 所以有回到了 监听对象的属性变化: 两种方式: 1、Proxy(vue3实现原理)、2、Object.defineProperty(value, property, receiver)
// depend.notify();
// reactiveFns.forEach((fn) => {
//   fn();
// });
4、依赖收集的管理
// 每个属性对应一个Depend对象
class Depend {
  constructor() {
    this.reactiveFns = [];
  }
  // 让每个属性对应一个Depend类
  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn);
  }
  notify() {
    this.reactiveFns.forEach((fn) => fn());
  }
}

// 封装一个响应式函数
// name发生改变的所有需要重新执行的函数, 数组不是很方便,如果里面有age呢, 我们该用类
// let reactiveFns = [];
const depend = new Depend();
function watchFn(fn) {
  // reactiveFns.push(fn);
  depend.addDepend(fn);
}

// 封装一个获取Depend函数
const targetMap = new WeakMap();
function getDepend(target, key) {
  // 1.根据target对象获取map的过程
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }
  // 2.根据key获取depend对象
  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

// 对象的响应式
const obj = {
  name: "chen",
  age: 23,
};

// 监听对象的属性变化:
// 两种方式: 1、Proxy(vue3实现原理)  2、Object.defineProperty(value, property, receiver)
const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    // 改变属性自动执行notify
    // depend.notify();
    const depend = getDepend(target, key);
    console.log(depend.reactiveFns);
    depend.notify();
  },
});

watchFn(function () {
  const newName = objProxy.name;
  console.log("你好");
  console.log(objProxy.name);
});

watchFn(function () {
  console.log(objProxy.name, "demo function----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化1----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化2----");
});

// 监听对象变化,执行三次,响应式
objProxy.name = "天线大爷";
objProxy.name = "天线大爷2";
objProxy.name = "天线大爷3";
objProxy.age = 200;

const info = {
  address: "深圳",
};
watchFn(function () {
  console.log(info.address, "address变化1------");
});
watchFn(function () {
  console.log(info.address, "address变化2------");
});

对象的依赖管理:
在这里插入图片描述

5、正确收集依赖
class Depend {
  constructor() {
    this.reactiveFns = [];
  }
  addDepend(reactiveFn) {
    this.reactiveFns.push(reactiveFn);
  }
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
}

// const depend = new Depend();
let activeReactiveFn = null;
function watchFn(fn) {
  // depend.addDepend(fn);
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

const targetMap = new WeakMap();
function getDepend(target, key) {
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }
  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

const obj = {
  name: "chen",
  age: 23,
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    // 根据target,key获取可以对应的depend
    const depend = getDepend(target, key);
    // 给depend对象添加响应函数
    depend.addDepend(activeReactiveFn);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    const depend = getDepend(target, key);
    console.log(depend.reactiveFns);
    depend.notify();
  },
});

watchFn(function () {
  console.log('第一次执行')
  console.log("你好name");
  console.log(objProxy.name);
});

watchFn(function () {
  console.log(objProxy.name, "name function----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化1----");
});
watchFn(function () {
  console.log(objProxy.age, "age 变化2----");
});

objProxy.name = "天线大爷";
// objProxy.name = "天线大爷2";
// objProxy.name = "天线大爷3";
// objProxy.age = 200;

// const info = {
//   address: "深圳",
// };
// watchFn(function () {
//   console.log(info.address, "address变化1------");
// });
// watchFn(function () {
//   console.log(info.address, "address变化2------");
// });
6、Depend类的优化
// let activeReactiveFn: 当前需要收集的响应式函数
let activeReactiveFn = null;

/**
 * Depend优化:
 *  1、depeng方法
 *  2、使用Set来保存依赖函数,而不是数组[]
 */
class Depend {
  constructor() {
    // this.reactiveFns = [];
    this.reactiveFns = new Set();
  }
  addDepend(reactiveFn) {
    this.reactiveFns.add(reactiveFn);
  }
  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn);
    }
  }
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
}

// const depend = new Depend();

function watchFn(fn) {
  // depend.addDepend(fn);
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

const targetMap = new WeakMap();
function getDepend(target, key) {
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }
  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

const obj = {
  name: "chen",
  age: 23,
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    const depend = getDepend(target, key);
    // depend.addDepend(activeReactiveFn);
    depend.depend();
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    const depend = getDepend(target, key);
    console.log(depend.reactiveFns);
    depend.notify();
  },
});

watchFn(() => {
  // 被执行了 两次,objProxy.name收集了两次。改成set就执行一次
  console.log(objProxy.name, "/==========");
  console.log(objProxy.name, "+++++++++++++");
});
objProxy.name = "陈胜";
7、对象的响应式操作(vue3原理)
let activeReactiveFn = null;

class Depend {
  constructor() {
    // this.reactiveFns = [];
    this.reactiveFns = new Set();
  }
  addDepend(reactiveFn) {
    this.reactiveFns.add(reactiveFn);
  }
  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn);
    }
  }
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
}

function watchFn(fn) {
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

const targetMap = new WeakMap();
function getDepend(target, key) {
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }
  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const depend = getDepend(target, key);
      depend.depend();
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      const depend = getDepend(target, key);
      console.log(depend.reactiveFns);
      depend.notify();
    },
  });
}

const obj = {
  name: "chen",
  age: 23,
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    const depend = getDepend(target, key);
    depend.depend();
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    const depend = getDepend(target, key);
    console.log(depend.reactiveFns);
    depend.notify();
  },
});

const info = {
  address: "深圳",
  height: 1.6,
};
const infoProxy = reactive(info);
watchFn(function () {
  console.log(infoProxy.address);
});

infoProxy.address = "广州";

// 简化操作
const foo = reactive({
  name: "foo",
});
watchFn(function () {
  console.log(foo.name + " hello");
});
foo.name = "bar";
8、对象的响应式操作(vue2原理)
let activeReactiveFn = null;

class Depend {
  constructor() {
    // this.reactiveFns = [];
    this.reactiveFns = new Set();
  }
  addDepend(reactiveFn) {
    this.reactiveFns.add(reactiveFn);
  }
  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn);
    }
  }
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
}

function watchFn(fn) {
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

const targetMap = new WeakMap();
function getDepend(target, key) {
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }
  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

function reactive(obj) {
  // es6之前监听对象属性
  Object.keys(obj).forEach((key) => {
    let value = obj[key];
    Object.defineProperty(obj, key, {
      get() {
        const depend = getDepend(obj, key);
        depend.depend();
        return value;
      },
      set(newValue) {
        value = newValue;
        const depend = getDepend(obj, key);
        depend.notify();
      },
    });
  });
  return obj;
}

const obj = {
  name: "chen",
  age: 23,
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    const depend = getDepend(target, key);
    depend.depend();
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    Reflect.set(target, key, value, receiver);
    const depend = getDepend(target, key);
    console.log(depend.reactiveFns);
    depend.notify();
  },
});

const info = {
  address: "深圳",
  height: 1.6,
};
const infoProxy = reactive(info);
watchFn(function () {
  console.log(infoProxy.address);
});

infoProxy.address = "广州";

// 简化操作
const foo = reactive({
  name: "foo",
});
watchFn(function () {
  console.log(foo.name + " hello");
});
foo.name = "bar";
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值