目录 1 什么是响应式2 响应式函数的封装3 类的封装4 数据结构的选择5 监听对象变化Vue2—Object.definePropertyVue3—Proxy 6 代码设计 1 什么是响应式本文详细介绍了响应式原理,手把手实现Vue中的响应式
响应式可以理解为:数据发生改变时,用到该数据的代码块自动重新执行。简而言之,就是A变,依赖A(用到A的值)的函数也跟着变
那么就需要依次考虑以下问题:
1 如何监听到A值的改变?:Proxy代理set捕获器可以监听值改变2 监听到之后,重新执行用到A相关的函数,那么如何知道哪些函数用到了A? 2.1 每个A都对应一个数组,里面存用到它的函数2.2 怎么知道函数用到A: 将普通函数封装为响应式函数封装过程中先调用一次函数,就可以在属性get *** 作中捕获该函数
2 响应式函数的封装
function foo(){
console.log(objProxy.name);
}
let activeReactiveFn = null; // 全局变量,保存当前执行的响应函数
watchFn(fn){
activeReactiveFn = fn;
fn(); // 执行一次 可以在用到对象的get方法中捕获
activeReactiveFn = null;
}
3 类的封装
每个对象的属性都对应一个数组,来保存依赖它的函数。对该数组主要进行两种 *** 作
添加元素:将新的依赖函数加入数组遍历数组:数据变化时,对数组遍历,执行每个函数由于是围绕一个数组,进行 *** 作,所以将数组和 *** 作封装为一个Depend类来管理
由于一个函数中可能多次用到对象的属性,只要执行一次就可以,所以要求数组中元素不能重复,因此选择Set集合来实现
class Depend {
constructor() {
this.reactiveFn = new Set();
}
depend() {
activeReactiveFn && this.addDepend(activeReactiveFn);
}
addDepend(fn) {
this.reactiveFn.add(fn);
}
notify() {
this.reactiveFn.forEach((fn) => fn());
}
}
4 数据结构的选择
每个对象的每个属性都对应一个集合,可以采用WeakMap,其中key为对象,value为map对象;每个map映射表的key保存对象的属性名,value保存对应的Depend类型对象
WeakMap的好处在于它对key(obj1和obj2)是弱引用,当key对象销毁时,不会影响GC对对象的回收
function reactive(obj) {
Object.keys(obj).forEach((key) => {
let value = obj[key];
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get() {
const depend = getDepend(obj, key);
depend.depend();
return value;
},
set(newValue) {
value = newValue;
const depend = getDepend(obj, key);
depend.notify();
},
});
});
return obj;
}
Vue3—Proxy
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, newValue, receiver) {
Reflect.set(target, key, newValue, receiver);
// 通知依赖
const depend = getDepend(target, key);
depend.notify();
},
});
}
6 代码设计
// 构造依赖类
class Depend {
constructor() {
this.reactiveFn = new Set();
}
depend() {
activeReactiveFn && this.addDepend(activeReactiveFn);
}
addDepend(fn) {
this.reactiveFn.add(fn);
}
notify() {
this.reactiveFn.forEach((fn) => fn());
}
}
// 查找/获取依赖
const targetMap = new WeakMap();
function getDepend(target, key) {
let map = targetMap.get(target);
if (!map) {
map = new Map();
targetMap.set(obj, map);
}
let depend = map.get(key);
if (!depend) {
depend = new Depend();
map.set(key, depend);
}
return depend;
}
// 响应函数封装函数
let activeReactiveFn = null;
function watchFn(fn) {
activeReactiveFn = fn;
fn();
activeReactiveFn = null;
}
// 监听对象变化vue3(也可以替换为vue2的版本
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, newValue, receiver) {
Reflect.set(target, key, newValue, receiver);
// 通知依赖
const depend = getDepend(target, key);
depend.notify();
},
});
}
const obj = {
name: "xs",
age: 18,
};
function foo() {
console.log(objProxy.name);
}
const objProxy = reactive(obj);
watchFn(foo);
// test
objProxy.name = "kobe";
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)