Vue3響應式API-reactive的數組代理


響應性基礎 API 之 reactive

基本用法

  • 返回對象的響應式副本,只能代理對象,不能代理普通值
const obj = reactive({count: 0, name: 'hyh'})

const count = reactive(1)
// value cannot be made reactive: 1

實現原理

reactive 是基於 Proxy 實現的響應式。


const ReactiveFlags = {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  RAW = '__v_raw'
}

// 用於緩存代理過的對象
const proxyMap = new WeakMap();

function createArrayInstrumentations(){
    const instrumentations = {};

    // proxy([a,b,c]).includes('a')   
    ;['includes', 'indexOf', 'lastIndexOf'].forEach(key =>{
        instrumentations[key] = function(this, ...args){
            // 將代理對象轉化成普通的數組,然后進行依賴收集
            const arr = toRaw(this)
            for(let i = 0, l = this.length; i < l; i++ ){
                // 依賴收集
                track(arr, 'get', i + '')
            }
            // 調用數組原來的方法
            const res = arr[key](...args)
            // we run the method using the original args first (which may be reactive)
            if(res === -1 || res === false){
                // 如果沒有找到 將參數轉化為原始類型重新執行
                /*
                    let obj = {a: 1};
                    let reactObj = reactive(obj)
                    const arr1 = reactive([obj])
                    console.log('arr1.includes(obj) :>> ', arr1.includes(reactObj));  == true
                */
                return arr[key](...args.map(toRaw))
            }else{
                return res
            }
        }
    })
    // instrument length-altering mutation methods to avoid length being tracked
    // which leads to infinite loops in some cases (#2137)
    ;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
        instrumentations[key] = function(this, ...args){
            pauseTracking();
            const res = toRaw(this).apply(this, args)
            resetTracking();
            return res
        }
    })

}

function createGetter(isReadonly = false, shallow = false){
    return function get(target, key, receiver){
        // 判斷代理對象是否為數組
        const targetIsArray = isArray(target);
        if(targetIsArray && hasOwn(arrayInstrumentations, key)){
            // 如果是特殊方法,沒有進行依賴收集, 而是重寫了一些方法
            return Reflect.get(arrayInstrumentations, key, receiver)
        }
        
       // ...對象處理
    }
}

function createSetter(shallow = false){
    return function set(target, key, value, receiver){
        const oldValue = target[key]

         // 判斷:是新增還是修改
        const hadKey = isArray(target) && isIntegerKey(key)
                ? Number(key) < target.length
                : hasOwn(target, key)
        const res = Reflect.set(target, key, value, receiver);

        // 觸發effect更新
        if(!hadKey){
            trigger(target, 'add', key, value)
        }else{
            trigger(target, 'set', key, value, oldValue)
        }

    }
}

function mutableHandler(){
    get: createGetter(),
    set: cerateSetter(),

}

function reactive(target){

    // 針對特殊場景 reactive(readonly(obj))-> 直接返回readonly即可
    if(target && target[ReactiveFlags.IS_READONLY]){
        return target
    }
    return createReactiveObject(target, false, mutaleHandler)
}

function createReactiveObject(target, isReadonly, baseHandler){
    if(!isObject(target)){
        if (__DEV__) {
            console.warn(`value cannot be made reactive: ${String(target)}`)
        }
        return target
    }

    // target already has corresponding Proxy
    const existingProxy = proxyMap.get(target) // 看目標是否被代理過
    if (existingProxy) { // 如果代理過,直接返回代理過的對象
        return existingProxy
    }

    const proxy = new Proxy( // 創建一個proxy對象
        target,// 調用對應的handler進行代理
        targetType: baseHandlers
    )
    proxyMap.set(target, proxy) // 存到緩存中
    return proxy
}

以上代碼只是Vue3的部分源碼,簡單的實現了reactive的響應式原理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM