目录
一、响应式概念
二、响应式函数的实现 watchFn
三、响应式依赖的收集
四、监听对象的变化
1. vue2
2. vue3
五、对象的依赖管理
1. 图解
2. 代码
六、响应式完整代码
一、响应式概念
- m有一个初始化的值,有一段代码使用了这个值
- 那么在m有一个新的值时,这段代码可以自动重新执行
这样一种可以自动响应数据变量的代码机制,就称之为是响应式的
二、响应式函数的实现 watchFn
- 封装一个新的函数watchFn
- 凡是传入到watchFn的函数,就是需要响应式的
- 其他默认定义的函数都是不需要响应式的
// 需要收集依赖的函数
let reactiveFn = null;
function watchFn(fn) {
reactiveFn = fn;
fn();
reactiveFn = null;
}
// 使用
watchFn(function foo() {
console.log('foo => name :', obj.name);
console.log('foo => age : ', obj.age);
});
三、响应式依赖的收集
设计一个类,这个类用于管理某一个对象的某一个属性的所有响应式函数
// 1. 创建一个Depend类
class Depend {
constructor() {
// 2. 创建一个数组,用来存储依赖
this.reactiveFns = new Set();
}
// 3. 用来通知Depend类中的依赖执行
notify() {
this.reactiveFns.forEach((fn) => {
fn();
});
}
// 4. 用来添加依赖
addDepend(fn) {
if (fn) {
this.reactiveFns.add(fn);
}
}
// 5. 也可以这么添加
depend() {
if (reactiveFn) {
this.reactiveFns.add(reactiveFn);
}
}
}
四、监听对象的变化
1. vue2
通过 Object.defineProperty的方式
// 拦截对象属性,使对象变成响应式
function reactive(obj) {
Object.keys(obj).forEach((key) => {
let value = obj[key];
// 找到对应的obj对象的key对应的dep对象
const dep = getDepend(obj, key);
// 设置set、get拦截
Object.defineProperty(obj, key, {
set(newValue) {
value = newValue;
// 修改数据后进行通知
dep.notify();
},
get() {
// 增加依赖
// dep.addDepend(reactiveFn);
dep.depend();
return value;
}
});
});
return obj;
}
2. vue3
通过new Proxy的方式
function reactive(obj) {
// 1. 创建代理对象
const objProxy = new Proxy(obj, {
set(target, key, newValue, receiver) {
// target[key] = newValue;
// 运用Reflect反射
Reflect.set(target, key, newValue, receiver);
const dep = getDepend(target, key);
// 通知变化
dep.notify();
},
get(target, key, receiver) {
const dep = getDepend(target, key);
// 收集依赖
dep.depend();
// return target[key];
return Reflect.get(target, key, receiver);
}
});
return objProxy;
}
五、对象的依赖管理
1. 图解
2. 代码
// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap();
function getDepend(obj, key) {
// 获取外层对象的map
let map = objMap.get(obj);
if (!map) {
map = new Map();
objMap.set(obj, map);
}
let dep = map.get(key);
if (!dep) {
dep = new Depend();
map.set(key, dep);
}
return dep;
}
六、响应式完整代码
// 1. 创建一个Depend类
class Depend {
constructor() {
// 2. 创建一个数组,用来存储依赖
this.reactiveFns = new Set();
}
// 3. 用来通知Depend类中的依赖执行
notify() {
this.reactiveFns.forEach((fn) => {
fn();
});
}
// 4. 用来添加依赖
addDepend(fn) {
if (fn) {
this.reactiveFns.add(fn);
}
}
// 5. 也可以这么添加
depend() {
if (reactiveFn) {
this.reactiveFns.add(reactiveFn);
}
}
}
// 需要收集依赖的函数
let reactiveFn = null;
function watchFn(fn) {
reactiveFn = fn;
fn();
reactiveFn = null;
}
// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap();
function getDepend(obj, key) {
// 获取外层对象的map
let map = objMap.get(obj);
if (!map) {
map = new Map();
objMap.set(obj, map);
}
let dep = map.get(key);
if (!dep) {
dep = new Depend();
map.set(key, dep);
}
return dep;
}
// 拦截对象属性,使对象变成响应式
/**
* vue2 : Object.defineProperty
*/
function reactive(obj) {
Object.keys(obj).forEach((key) => {
let value = obj[key];
// 找到对应的obj对象的key对应的dep对象
const dep = getDepend(obj, key);
// 设置set、get拦截
Object.defineProperty(obj, key, {
set(newValue) {
value = newValue;
// 修改数据后进行通知
dep.notify();
},
get() {
// 增加依赖
// dep.addDepend(reactiveFn);
dep.depend();
return value;
}
});
});
return obj;
}
/**
* vue3 : new Proxy
*/
function reactive3(obj) {
// 1. 创建代理对象
const objProxy = new Proxy(obj, {
set(target, key, newValue, receiver) {
// target[key] = newValue;
// 运用Reflect反射
Reflect.set(target, key, newValue, receiver);
const dep = getDepend(target, key);
// 通知变化
dep.notify();
},
get(target, key, receiver) {
const dep = getDepend(target, key);
// 收集依赖
dep.depend();
// return target[key];
return Reflect.get(target, key, receiver);
}
});
return objProxy;
}
/**
* 业务代码
*/
const obj = reactive({
name: 'star',
age: 18
});
watchFn(function foo() {
console.log('foo => name :', obj.name);
console.log('foo => age : ', obj.age);
});
watchFn(function bar() {
console.log('bar => age : ', obj.age + 10);
});
obj.name = 'coder';
// obj.age = 20;
const user = reactive({
name: 'aaa'
});
watchFn(function () {
console.log('baz:', user.name);
});
user.name = 'bbb';