中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue3中的響應式原理effect怎么實現

發布時間:2022-08-13 09:52:59 來源:億速云 閱讀:219 作者:iii 欄目:開發技術

本篇內容主要講解“vue3中的響應式原理effect怎么實現”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“vue3中的響應式原理effect怎么實現”吧!

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默認執行
}

依賴收集

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怎么實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

南雄市| 遵义市| 六枝特区| 德化县| 渝北区| 马关县| 普安县| 大城县| 修文县| 衢州市| 博白县| 镇雄县| 贺州市| 且末县| 景洪市| 宜兴市| 库伦旗| 射洪县| 库车县| 勃利县| 资兴市| 疏附县| 滦南县| 永春县| 沂源县| 长垣县| 大新县| 南川市| 和田市| 水富县| 全州县| 河源市| 青浦区| 兴国县| 湘西| 郯城县| 修武县| SHOW| 双柏县| 牡丹江市| 中西区|