您的位置:首页 > 技术中心 > 前端框架 >

vue3中的响应式原理effect怎么实现

时间:2023-05-10 15:02

effect的基本实现

export let activeEffect = undefined;// 当前正在执行的effectclass 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,这样后续可以用于清理        }    }}

将属性和对应的effect维护成映射关系,后续属性变化可以触发对应的effect函数重新run

触发更新

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

分支切换与cleanup

在渲染时我们要避免副作用函数产生的遗留

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

这里要注意的是:触发时会进行清理操作(清理effect),在重新进行收集(收集effect)。在循环过程中会导致死循环。

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

停止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}

调度执行

trigger触发时,我们可以自己决定副作用函数执行的时机、次数、及执行方式

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;}

当取值时返回的值是对象,则返回这个对象的代理对象,从而实现深度代理

以上就是vue3中的响应式原理effect怎么实现的详细内容,更多请关注Gxl网其它相关文章!

热门排行

今日推荐

热门手游