Home > Web Front-end > Vue.js > body text

How to implement the responsive principle effect in vue3

WBOY
Release: 2023-05-10 10:52:06
forward
1536 people have browsed it

Basic implementation of effect

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默认执行
}
Copy after login

Dependency collection

get(target, key, receiver) {
    if (key === ReactiveFlags.IS_REACTIVE) {
        return true;
    }
    const res = Reflect.get(target, key, receiver);
    track(target, 'get', key);  // 依赖收集
    return res;
}
Copy after login
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,这样后续可以用于清理
        }
    }
}
Copy after login

Maintain a mapping relationship between attributes and corresponding effects. Subsequent attribute changes can trigger the corresponding effect function to rerun

Trigger update

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;
}
Copy after login
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(); // 防止循环
    })
}
Copy after login

Branch switching and cleanup

We need to avoid the legacy of side-effect functions when rendering

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)
Copy after login
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(); // 先清理在运行
        }
    }
}
Copy after login

What should be noted here is: the cleanup operation will be performed when triggered (Cleaning effect), and then collecting again (collecting effect). An infinite loop will occur during the loop.

let effect = () => {};
let s = new Set([effect])
s.forEach(item=>{s.delete(effect); s.add(effect)}); // 这样就导致死循环了
Copy after login

Stop 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
}
Copy after login

Scheduling execution

When the trigger is triggered, we can decide the timing, number, and execution method of the side effect function

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(); 
                }
            }
        }
    }
}
Copy after login

Deep proxy

get(target, key, receiver) {
    if (key === ReactiveFlags.IS_REACTIVE) {
        return true;
    }
    // 等会谁来取值就做依赖收集
    const res = Reflect.get(target, key, receiver);
    track(target, &#39;get&#39;, key);
    if(isObject(res)){
        return reactive(res);
    }
    return res;
}
Copy after login

When the value returned is an object, the proxy object of this object is returned, thereby realizing deep proxy

The above is the detailed content of How to implement the responsive principle effect in vue3. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template