export let activeEffect = undefined;// 当前正在执行的effect class ReactiveEffect { active = true; deps = []; // 收集effect中使用到的属性 parent = undefined; constructor(public fn) { } run() { if (!this.active) { // 不是激活状态 return this.fn(); } try { this.parent = activeEffect; // 当前的effect就是他的父亲 activeEffect = this; // 设置成正在激活的是当前effect return this.fn(); } finally { activeEffect = this.parent; // 执行完毕后还原activeEffect this.parent = undefined; } } } export function effect(fn, options?) { const _effect = new ReactiveEffect(fn); // 创建响应式effect _effect.run(); // 让响应式effect默认执行 }
get(target, key, receiver) { if (key === ReactiveFlags.IS_REACTIVE) { return true; } const res = Reflect.get(target, key, receiver); track(target, 'get', key); // 依赖收集 return res; }
const targetMap = new WeakMap(); // 记录依赖关系 export function track(target, type, key) { if (activeEffect) { let depsMap = targetMap.get(target); // {对象:map} if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key); if (!dep) { depsMap.set(key, (dep = new Set())) // {对象:{ 属性 :[ dep, dep ]}} } let shouldTrack = !dep.has(activeEffect) if (shouldTrack) { dep.add(activeEffect); activeEffect.deps.push(dep); // 让effect记住dep,这样后续可以用于清理 } } }
Mengekalkan hubungan pemetaan antara atribut dan kesan yang sepadan Perubahan atribut seterusnya boleh mencetuskan fungsi kesan yang sepadan untuk dijalankan semula
set(target, key, value, receiver) { // 等会赋值的时候可以重新触发effect执行 let oldValue = target[key] const result = Reflect.set(target, key, value, receiver); if (oldValue !== value) { trigger(target, 'set', key, value, oldValue) } return result; }
export function trigger(target, type, key?, newValue?, oldValue?) { const depsMap = targetMap.get(target); // 获取对应的映射表 if (!depsMap) { return } const effects = depsMap.get(key); effects && effects.forEach(effect => { if (effect !== activeEffect) effect.run(); // 防止循环 }) }
Kita perlu mengelakkan warisan fungsi kesan sampingan semasa membuat
const state = reactive({ flag: true, name: 'jw', age: 30 }) effect(() => { // 副作用函数 (effect执行渲染了页面) console.log('render') document.body.innerHTML = state.flag ? state.name : state.age }); setTimeout(() => { state.flag = false; setTimeout(() => { console.log('修改name,原则上不更新') state.name = 'zf' }, 1000); }, 1000)
function cleanupEffect(effect) { const { deps } = effect; // 清理effect for (let i = 0; i < deps.length; i++) { deps[i].delete(effect); } effect.deps.length = 0; } class ReactiveEffect { active = true; deps = []; // 收集effect中使用到的属性 parent = undefined; constructor(public fn) { } run() { try { this.parent = activeEffect; // 当前的effect就是他的父亲 activeEffect = this; // 设置成正在激活的是当前effect + cleanupEffect(this); return this.fn(); // 先清理在运行 } } }
Apa yang perlu diperhatikan di sini ialah: operasi pembersihan akan dilakukan apabila dicetuskan (Kesan pembersihan), dan kemudian mengumpul semula (kesan pengumpulan). Gelung tak terhingga akan berlaku semasa gelung.
let effect = () => {}; let s = new Set([effect]) s.forEach(item=>{s.delete(effect); s.add(effect)}); // 这样就导致死循环了
export class ReactiveEffect { stop(){ if(this.active){ cleanupEffect(this); this.active = false } } } export function effect(fn, options?) { const _effect = new ReactiveEffect(fn); _effect.run(); const runner = _effect.run.bind(_effect); runner.effect = _effect; return runner; // 返回runner }
Apabila pencetus dicetuskan, kita boleh menentukan pemasaan, nombor dan kaedah pelaksanaan fungsi kesan sampingan
export function effect(fn, options:any = {}) { const _effect = new ReactiveEffect(fn,options.scheduler); // 创建响应式effect // if(options){ // Object.assign(_effect,options); // 扩展属性 // } _effect.run(); // 让响应式effect默认执行 const runner = _effect.run.bind(_effect); runner.effect = _effect; return runner; // 返回runner } export function trigger(target, type, key?, newValue?, oldValue?) { const depsMap = targetMap.get(target); if (!depsMap) { return } let effects = depsMap.get(key); if (effects) { effects = new Set(effects); for (const effect of effects) { if (effect !== activeEffect) { if(effect.scheduler){ // 如果有调度函数则执行调度函数 effect.scheduler() }else{ effect.run(); } } } } }
get(target, key, receiver) { if (key === ReactiveFlags.IS_REACTIVE) { return true; } // 等会谁来取值就做依赖收集 const res = Reflect.get(target, key, receiver); track(target, 'get', key); if(isObject(res)){ return reactive(res); } return res; }
Apabila nilai yang dikembalikan ialah objek, objek proksi objek ini dikembalikan, dengan itu merealisasikan Proksi Kedalaman
Atas ialah kandungan terperinci Bagaimana untuk melaksanakan kesan prinsip responsif dalam vue3. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!