前言
之前对Vue3的响应式源码进行比较详细的解读,这次就来手动实现一个简单的响应式
响应式原理
我们首先来通过流程图整体回顾下Vue3 Reactivity的整体流程
代码实现
工具函数
// 判断是否是对象
function isObject(val) {
return typeof val === 'object' && val !== null
}
// 对象是否有某个属性
function hasOwn(target,key) {
return target.hasOwnProperty(key)
}
数据存储
// 已有代理的存储
let toProxy = new WeakMap();
let toRaw = new WeakMap();
// 当前激活的effect栈
let activeEffectstack = []
// 依赖映射表
let targetMap = new Map()
主要逻辑
// 创建响应式对象
function reactive(target) {
return createReactiveObj(target)
}
function createReactiveObj(target) {
// 仅接受Object对象
if(!isObject(target)) {
return target;
}
// 检查是否已代理过该对象
let proxy = toProxy.get(target);
if(proxy) {
return proxy
}
if(toRaw.has(target)) {
return target;
}
// 使用proxy代理
let observed = new Proxy(target,baseHandler);
// 维护proxy表
toProxy.set(target,observed);
toRaw.set(observed,target);
return observed;
}
track & trigger
function track(target,key) {
// 当前激活栈的栈顶就是当前激活的effect
const effect = activeEffectstack[activeEffectstack.length -1];
if(effect) {
// 维护依赖表
let depsMap = targetMap.get(target);
if(!depsMap) {
targetMap.set(target,(depsMap = new Map()))
}
let deps = depsMap.get(key)
if(!deps) {
depsMap.set(key,(deps = new Set()))
}
if(!deps.has(effect)) {
deps.add(effect)
}
}
}
function trigger(target,key,type) {
// 从依赖表中获取effect并逐个执行
let depsMap = targetMap.get(target)
if(depsMap) {
let deps = depsMap.get(key)
if(deps) {
deps.forEach((effect)=>{
effect();
})
}
}
}
effect
// 依赖函数
function effect(fn) {
const effectF = function () {
try {
// 加入effect栈
activeEffectstack.push(effectF)
return fn()
} finally {
activeEffectstack.pop();
}
}
// 创建effect就会触发一次
effectF();
return effectF
}
演示
let obj = reactive({name: 'raw'});
effect(()=>{
console.log(obj.name);
})
obj.name = 'observed'