欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3源碼分析reactivity實現(xiàn)方法示例

 更新時間:2023年01月18日 16:02:15   作者:豬豬愛前端  
這篇文章主要為大家介紹了Vue3源碼分析reactivity實現(xiàn)方法原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

深入分析對于map、set、weakMap、weakSet的響應式攔截

在上篇的內(nèi)容中我們以reactive為起點分析了reactivity對于array和object的攔截,本文我們繼續(xù)以reactive為起點分析map、set、weakMap、weakSet等數(shù)據(jù)結構的響應式攔截。

export function shallowReactive(target) {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers,
    shallowCollectionHandlers,
    shallowReactiveMap
  );
}
export function readonly(target) {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  );
}
export function shallowReadonly(target) {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers,
    shallowReadonlyCollectionHandlers,
    shallowReadonlyMap
  );
}
export function reactive(target) {
  //如果被代理的是readonly返回已經(jīng)被readonly代理過的target
  if (isReadonly(target)) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  );
}
  • 之前我們分析了mutableHandlers、shallowReadonlyHandlers、readonlyHandlers、shallowReactiveHandlers,但是還有一個部分是沒有分析的也就是對于集合類型的處理mutableCollectionHandlers、shallowReadonlyCollectionHandlers、readonlyCollectionHandlers、shallowCollectionHandlers下面我們看看這四個對象的廬山真面目吧!
const mutableCollectionHandlers = {
  get: createInstrumentationGetter(false, false),
};
const shallowCollectionHandlers = {
  get: createInstrumentationGetter(false, true),
};
const readonlyCollectionHandlers = {
  get: createInstrumentationGetter(true, false),
};
const shallowReadonlyCollectionHandlers = {
  get: createInstrumentationGetter(true, true),
};
  • 我們可以看到所有的collectionHandlers都是由工廠函數(shù)createInstrumentationGetter創(chuàng)建的,這里與之前的handlers不同,所有的攔截都只有一個方法了那就是get,這是因為對于map set等數(shù)據(jù)結構的操作與object和array的操作是不同的,對于set需要調(diào)用add,delete,has等方法map需要調(diào)用set,delete,has等方法所以不能直接對集合數(shù)據(jù)類型進行操作,那么我們就只需要攔截get獲取到當前集合調(diào)用的方法然后對這個方法進行攔截就可以了。
function createInstrumentationGetter(isReadonly, shallow) {
  const instrumentations = shallow
    ? isReadonly
      ? shallowReadonlyInstrumentations
      : shallowInstrumentations
    : isReadonly
    ? readonlyInstrumentations
    : mutableInstrumentations;
  return (target, key, receiver) => {
    //對于map set的代理同樣需要添加
    if (key === IS_REACTIVE) {
      return !isReadonly;
    } else if (key === IS_READONLY) {
      return isReadonly;
    } else if (key === RAW) {
      return target;
    }
    //通過之前生成的攔截方法進行調(diào)度
    return Reflect.get(
      hasOwn(instrumentations, key) && key in target
        ? instrumentations
        : target,
      key,
      receiver
    );
  };
}
  • 對于和之前相同的屬性判斷我們就不再贅述了,直接看mutableInstrumentations、readonlyInstrumentations、shallowInstrumentations、shallowReadonlyInstrumentations通過readonly和shallow的不同得到不同的處理器。那我們就需要看看這四個對象是如何生成的了。
//通過攔截map set的方法實現(xiàn)代理
export function createInstrumentations() {
  const mutableInstrumentations = {
  };
  const shallowInstrumentations = {
  };
  const readonlyInstrumentations = {
  };
  const shallowReadonlyInstrumentations = {
  };
  //其中keys,values,entries,Symbol.iterator是通過
  //迭代器運行的,需要進行攔截
  const iteratorMethods = ["keys", "values", "entries", Symbol.iterator];
  iteratorMethods.forEach((method) => {
    mutableInstrumentations[method] = createIterableMethod(
      method,
      false,
      false
    );
    readonlyInstrumentations[method] = createIterableMethod(
      method,
      true,
      false
    );
    shallowInstrumentations[method] = createIterableMethod(method, false, true);
    shallowReadonlyInstrumentations[method] = createIterableMethod(
      method,
      true,
      true
    );
  });
  return [
    mutableInstrumentations,
    readonlyInstrumentations,
    shallowInstrumentations,
    shallowReadonlyInstrumentations,
  ];
}

下面我們需要將內(nèi)容分成四個部分,分別解讀這四個對象的方法實現(xiàn)。

(1).mutableInstrumentations

 const mutableInstrumentations = {
    get(key) {
      return get(this, key);
    },
    get size() {
      return size(this);
    },
    has: has,
    add,
    set: set,
    delete: deleteEntry,
    clear,
    forEach: createForEach(false, false),
};
  • 對于mutableInstrumentations的實現(xiàn)有get方法,這其實就是獲取元素的方法,我們需要對這個方法進行攔截。
  • 簡單的說,其實就是對set map的操作方法進行攔截,然后在獲取值的時候進行收集依賴,在修改值的時候觸發(fā)依賴核心依然沒有改變。但是需要注意的是map的的key可以是對象,還有可能是代理對象,但是無論是對象還是代理對象我們都應該只能訪問到唯一的那個值。

下面我們開始解讀get方法。

//代理map set weakMap weakSet的get方法
function get(target, key, isReadonly = false, isShallow = false) {
  target = target[RAW];
  //因為map的key可以是對象,所以需要rawKey
  //同時收集依賴必須要rawTarget
  const rawTarget = toRaw(target);
  const rawKey = toRaw(key);
  if (!isReadonly) {
    /**
     * 為了實現(xiàn)在effect函數(shù)中無論是使用了以proxyKey
     * 還是以rawKey為鍵進行收集的依賴,在effect外部
     * 修改proxyMap的proxyKey或rawKey都能觸發(fā)依賴
     * 更新,當使用proxyKey為鍵時,需要進行兩次track
     * 例如:當前在effect中獲取的是proxyKey那么進行
     * 兩次track,在depsMap中就會有兩個entries,分別
     * 是以rawKey和proxyKey指向的deps但是指向的deps
     * 不改變 那么在set中修改值的時候,無論是修改的
     * proxyKey還是rawKey都能在depsMap中找到正確的
     * 依賴進行更新
     */
    if (key !== rawKey) {
      track(rawTarget, trackOpTypes.get, key);
    }
    track(rawTarget, trackOpTypes.get, rawKey);
  }
  const { has } = getProto(rawTarget);
  const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
  //無論是使用rawKey還是key都能讀取到
  if (has.call(rawTarget, key)) {
    //僅需進行代理,并且返回代理后的對象
    return wrap(target.get(key));
  } else if (has.call(rawTarget, rawKey)) {
    return wrap(target.get(rawKey));
  } else if (target !== rawTarget) {
    target.get(key);
  }
}
  • 我們可以發(fā)現(xiàn)依賴收集觸發(fā)了兩次,當proxyKeykey的時候需要多觸發(fā)一次依賴收集,這是為了保證后續(xù)無論是通過rawKey修改值還是通過proxyKey修改值最終都能觸發(fā)到依賴。
  • 同樣我們處在get當中,無論訪問proxyKey還是rawKey我們都只能返回唯一的值。所以做了if elseif的判斷。

接下來繼續(xù)分析size方法:

//對map set的size屬性的攔截
function size(target, isReadonly = false) {
  target = target[RAW];
  !isReadonly && track(toRaw(target), trackOpTypes.iterate, ITERATE_KEY);
  return Reflect.get(target, trackOpTypes.size, target);
}
  • size屬于屬性的訪問,所以肯定是進行track,這里的target都會調(diào)用toRaw,之前在proxy中傳遞給我們的對象本來就是代理前的對象所以不需要toRaw,但是當前我們是對方法進行的攔截所以this訪問到的是代理后的對象所以需要對對象進行還原。
  • 這里就是對 "iterate" 進行了收集依賴,也就是說如果說執(zhí)行set delete add clear都會觸發(fā)這個依賴。具體可以看看后面對于這幾個方法的實現(xiàn)。

下面繼續(xù)分析has方法:

//has進行依賴收集
function has(key, isReadonly = false) {
  const target = this[RAW];//獲取代理前的對象
  const rawTarget = toRaw(target);
  const rawKey = toRaw(key);//獲取代理前的key
  if (!isReadonly) {
    //這里執(zhí)行兩次track的原因和上面相同
    if (key !== rawKey) {
      //收集依賴,類型為"has"
      track(rawTarget, trackOpTypes.has, key);
    }
    track(rawTarget, trackOpTypes.has, rawKey);
  }
  return key === rawKey
    ? target.has(key)
    : target.has(key) || target.has(rawKey);
}
  • 其實這個type主要是傳遞上下文信息到onTrigger中(如果effect中有這個函數(shù)),所以本質(zhì)都是通過target和key收集依賴。這個函數(shù)很簡單就不在過多描述了。

繼續(xù)add的分析:

//對set的add方法的攔截
function add(value) {
  value = toRaw(value); //獲取rawValue
  const target = toRaw(this); //獲取rawTarget
  const proto = getProto(target);
  //如果不存在這個值則是修改進行trigger
  const hadKey = proto.has.call(target, value);
  if (!hadKey) {
    target.add(value);
    trigger(target, triggerOpTypes.add, value, value);
  }
  return this;
}

我們來看看對于 "add" 類型的trigger處理:

case triggerOpTypes.add:
 if (!isArray(target)) {
   //map weakMap object
   deps.push(depsMap.get(ITERATE_KEY));
   if (isMap(target)) {
     deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
   }
 } else if (isIntegerKey(key)) {
   //當前修改的是數(shù)組且是新增值
   //例如 arr.length = 3 arr[4] = 8
   //此時數(shù)組長度會發(fā)生改變所以當前數(shù)組的
   //length屬性依然需要被放入依賴
   deps.push(depsMap.get("length"));
}
break;
  • 觸發(fā)關于迭代器的依賴,例如在effect中執(zhí)行了Object.keys map.entries map.keys等方法,那么ITERATE_KEY、MAP_KEY_ITERATE_KEY就會收集到相應的依賴函數(shù)。 繼續(xù)set的分析:
//這里的key可能是rawKey 也可能是proxyKey
function set(key, value) {
  value = toRaw(value); //獲取原始的value值
  const target = toRaw(this); //獲取原始的target
  const { has, get } = getProto(target);
  //判斷當前使用的key能否獲得值
  let hadKey = has.call(target, key);
  //獲取不到可能是proxyKey,轉化為rawKey再試試
  if (!hadKey) {
    key = toRaw(key);
    hadKey = has.call(target, key);
  } else {
    checkIdentityKeys(target, has, key);
  }
  //通過key獲取
  const oldValue = get.call(target, key);
  //設置
  target.set(key, value);
  //rawKey和proxyKey都獲取不到則是添加屬性
  if (!hadKey) {
    //觸發(fā)更新
    trigger(target, triggerOpTypes.add, key, value);
  }
  //修改屬性
  else if (hasChanged(value, oldValue)) {
    trigger(target, triggerOpTypes.set, key, value, oldValue);
  }
  return this;
}

object和array類似,但是依然需要處理proxyKey和rawKey的問題,如果proxyKey讀取到了值則不使用rawKey如果讀取不到轉化為rawKey繼續(xù)讀取,然后根據(jù)hadKey判斷是增加還是修改。

繼續(xù)分析delete 和 clear:

function deleteEntry(key) {
  const target = toRaw(this);
  const { has, get } = getProto(target);
  //刪除的key可能是proxyKey也可能是rawKey
  //所以需要判斷,判斷的時候時候需要使用has
  //方法,所以需要對target還原,實際上所有的
  //操作都不能使用receiver,會造成二次依賴觸發(fā)
  let hadKey = has.call(target, key);
  if (!hadKey) {
    key = toRaw(key);
    hadKey = has.call(target, key);
  } else {
    checkIdentityKeys(target, has, key);
  }
  const oldValue = get ? get.call(target, key) : undefined;
  const result = target.delete(key);
  //刪除觸發(fā)更新
  if (hadKey) {
    trigger(target, triggerOpTypes.delete, key, undefined, oldValue);
  }
  return result;
}
function clear() {
  const target = toRaw(this);
  const hadItems = target.size !== 0;
  //執(zhí)行clear后 數(shù)據(jù)會被全部清空,oldTarget將不再存在
  //所以需要淺克隆保證舊數(shù)據(jù)依然能進入trigger
  const oldTarget = isMap(target) ? new Map(target) : new Set(target);
  const result = target.clear();
  if (hadItems) {
    trigger(target, triggerOpTypes.clear, undefined, undefined, oldTarget);
  }
  return result;
}
  • delete和clear都是刪除元素,所以是觸發(fā)依賴,看看trigger對于delete和clear的類型的處理:
//clear
if (type === triggerOpTypes.clear) {
  //清空,相當于所有的元素都發(fā)生改變
  //故而全部都需要添加進依賴
  deps = [...depsMap.values()];
}
//delete
case triggerOpTypes.delete:
   if (!isArray(target)) {
    deps.push(depsMap.get(ITERATE_KEY));
    if (isMap(target)) {
       deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
    }
   }
break;
  • 對于clear因為所有元素都被刪除了,所以所有元素的依賴都需要被觸發(fā)。
  • 對于delete,則是觸發(fā)執(zhí)行了forEach、entries keys values等方法的依賴。當然刪除元素本身的依賴同樣需要被執(zhí)行。

最后一個forEach:

function createForEach(isReadonly, isShallow) {
  return function forEach(callback, thisArg) {
    const observed = this;
    const target = observed["__v_raw" /* ReactiveFlags.RAW */];
    const rawTarget = toRaw(target);
    const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
    !isReadonly &&
      track(rawTarget, "iterate" /* TrackOpTypes.ITERATE */, ITERATE_KEY);
    return target.forEach((value, key) => {
      return callback.call(thisArg, wrap(value), wrap(key), observed);
    });
  };
}
  • 當調(diào)用了forEach函數(shù) 也就是Map.forEach或者Set.forEach,這個也是靠迭代器所以依賴的收集則是ITERATE_KEY。 好了,到目前為止所有的api都已經(jīng)分析完成了。收集依賴的方法是get has size forEach entries keys values,觸發(fā)依賴則是clear set delete add。forEach、size、entries、keys、values方法會收集ITERATE_KEY或MAP_KEY_ITERATE_KEY的依賴。delete add set則會調(diào)用迭代器的依賴,換句話說就是集合的元素增加減少都會調(diào)用迭代器收集的依賴。

(2).shallowInstrumentations

const shallowInstrumentations = {
    get(key) {
      return get(this, key, false, true);
    },
    get size() {
      return size(this);
    },
    has: has,
    add,
    set: set,
    delete: deleteEntry,
    clear,
    forEach: createForEach(false, true),
};
  • 傳遞readonly、shallow生成不同的get和forEach。

(3).readonlyInstrumentations

 const readonlyInstrumentations = {
    get(key) {
      return get$1(this, key, true);
    },
    get size() {
      return size(this, true);
    },
    has(key) {
      return has.call(this, key, true);
    },
    //只讀的屬性是不需要修改的,全部通過warn提示
    add: createReadonlyMethod(triggerOpTypes.add),
    set: createReadonlyMethod(triggerOpTypes.set),
    delete: createReadonlyMethod(triggerOpTypes.delete),
    clear: createReadonlyMethod(triggerOpTypes.clear),
    forEach: createForEach(true, false),
  };
function createReadonlyMethod(type) {
  return function (...args) {
    {
      const key = args[0] ? `on key "${args[0]}" ` : ``;
      console.warn(
        `${shared.capitalize(
          type
        )} operation ${key}failed: target is readonly.`,
        toRaw(this)
      );
    }
    return type === triggerOpTypes.delete ? false : this;
  };
}
  • 對于readonly類型不能夠修改所以只要訪問set add delete clear等方法就會發(fā)出警告并且不能修改。

(4).shallowReadonlyInstrumentations

const shallowReadonlyInstrumentations = {
    get(key) {
      return get(this, key, true, true);
    },
    get size() {
      return size(this, true);
    },
    has(key) {
      return has.call(this, key, true);
    },
    //只讀的屬性是不需要修改的,全部通過warn提示
    add: createReadonlyMethod(triggerOpTypes.add),
    set: createReadonlyMethod(triggerOpTypes.set),
    delete: createReadonlyMethod(triggerOpTypes.delete),
    clear: createReadonlyMethod(triggerOpTypes.clear),
    forEach: createForEach(true, true),
  };

與第三種情況相同。

當然對于entries values keys Symbol.iterator的攔截還沒有分析,我們繼續(xù)看看實現(xiàn)的源碼:

function createIterableMethod(method, isReadonly, isShallow) {
  return function (...args) {
    const target = this[RAW];
    const rawTarget = toRaw(target);
    const targetIsMap = isMap(rawTarget); //被代理對象是否是map
    //如果是entries方法,會返回key和value
    const isPair =
      method === "entries" || (method === Symbol.iterator && targetIsMap);
    const isKeyOnly = method === "keys" && targetIsMap;
    //調(diào)用這個方法,返回迭代器
    const innerIterator = target[method](...args);
    //獲取當前需要代理的函數(shù)
    const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
    //readonly不需要track
    !isReadonly &&
      //追蹤
      track(
        rawTarget,
        trackOpTypes.iterate,
        //如果是Map且訪問的keys方法則是MAP_KEY_ITERATE_KEY
        isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
      );
    return {
      //重寫迭代器方法 key,value還可以被深度代理
      next() {
        const { value, done } = innerIterator.next();
        return done
          ? { value, done }
          : {
              //如果是entries方法value則是key和value
              value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
              done,
            };
      },
      [Symbol.iterator]() {
        return this;
      },
    };
  };
}

總結一下:對于map set weakMap weakSet的攔截,主要處理的有兩個地方:

  • 第一:對于mapweakMap類型,他們的key可能是一個對象,那么對象就可能是被代理過的對象,但是無論通過proxyKey訪問還是rawKey訪問到的對象都是一樣的,同樣的在effect中使用proxyKey,那么會觸發(fā)依賴收集,這個時候會存放進行兩次track,保證在effect外部修改proxy值的時候,無論是使用proxyKey修改還是rawKey修改最后都能正確觸發(fā)依賴。
  • 第二:當時用entries keys values forEach等集合方法的時候,收集依賴的key則是ITERATE_KEYMAP_KEY_ITERATE_KEY,當進行add delete set操作的時候會多添加在ITERATE_KEYMAP_KEY_ITERATE_KEY時收集到的依賴,保證了即使使用集合方法或者迭代器依然能夠進行依賴收集和觸發(fā)。
  • 第三:整個reactivity的核心依然沒有改變,只是攔截變成了攔截操作數(shù)據(jù)的方法,依舊是訪問的時候收集依賴,修改的時候觸發(fā)依賴。

ref、computed等方法的實現(xiàn)

(1).ref與shallowRef源碼解析

上面我們講述了對于對象數(shù)組等數(shù)據(jù)的代理,但是如果是string、number等基本數(shù)據(jù)類型呢?我們就需要采用ref這個api來實現(xiàn)代理了。我們先來看看refshallowRef的源碼實現(xiàn):

//判斷當前r是否是ref
function isRef(r) {
    //根本就是判斷當前對象上是否有__v_isRef屬性
    return !!(r && r.__v_isRef === true);
}
function ref(value) {
    //創(chuàng)建ref的工廠函數(shù),第二個參數(shù)為是為為shallow
    return createRef(value, false);
}
function shallowRef(value) {
    //第二個參數(shù)為true表示當前是shallow
    return createRef(value, true);
}
//如果是ref則返回ref,只對非ref進行代理
function createRef(rawValue, shallow) {
    if (isRef(rawValue)) {
        return rawValue;
    }
    return new RefImpl(rawValue, shallow);
}

這一段代碼非常簡單,就是通過工廠函數(shù) createRef(value,isShallow) 傳遞當前需要代理的基本數(shù)據(jù)類型以及是否只需要代理第一層。我們接著向下分析,看看RefImpl實現(xiàn)吧!。

class RefImpl {
    constructor(value, __v_isShallow) {
        //是否由shallowRef創(chuàng)建
        this.__v_isShallow = __v_isShallow;
        //這個dep和target,key對應的dep是一個意思
        //可以理解為target = this;key="value"對應的dep
        this.dep = undefined;
        this.__v_isRef = true;//是否是ref
        //未代理的value
        this._rawValue = __v_isShallow ? value : toRaw(value);
        //代理過后的value
        this._value = __v_isShallow ? value : toReactive(value);
    }
    get value() {
        //收集所有的依賴
        trackRefValue(this);
        return this._value;
    }
    set value(newVal) {
        //是否還需要進行深度代理
        const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal);
        newVal = useDirectValue ? newVal : toRaw(newVal);
        //如果當前值發(fā)生了修改相當于Object.is
        if (shared.hasChanged(newVal, this._rawValue)) {
            this._rawValue = newVal;
            this._value = useDirectValue ? newVal : toReactive(newVal);
            //觸發(fā)依賴更新
            triggerRefValue(this, newVal);
        }
    }
}
//兩個工具函數(shù)
const toReactive = (value) => shared.isObject(value) ? reactive(value) : value;
const toReadonly = (value) => shared.isObject(value) ? readonly(value) : value;
  • 我們可以發(fā)現(xiàn)這里的攔截只有getset了,當然也不需要deleteProperty has ownKeys的攔截了,所以我們通過類自帶的攔截器進行攔截,同樣的邏輯get的時候收集依賴,set的時候觸發(fā)依賴。
function trackRefValue(ref) {
  //判斷當前activeEffect是否存在不存在則不需要收集依賴
  if (shouldTrack && activeEffect) {
    ref = toRaw(ref);
    //收集target為ref key為"value"的依賴
    trackEffects(ref.dep || (ref.dep = createDep()), {
         target: ref,//target相當于ref
         type: "get",//類型是"get"
         key: 'value'//key是"value"
       });
    }
}
function triggerRefValue(ref, newVal) {
 ref = toRaw(ref);
 if (ref.dep) {   
   //觸發(fā)target為ref key為"value"的依賴
   triggerEffects(ref.dep, {
     target: ref,
     type: "set" /* TriggerOpTypes.SET */,
     key: 'value',
     newValue: newVal
   });     
 }
}
  • 我們可以發(fā)現(xiàn)整個ref的設計相當?shù)暮唵?就是把需要代理的基本數(shù)據(jù)類型變?yōu)橐粋€對象,然后再代理keyvalue值。

(2).toRefs

這是為了解決解構之后的proxy失去代理作用的api,例如:

const proxy = reactive({a:1,b:2})
const {a,b} = proxy //失效

這樣就失效了,但是如果你代理的是兩層解構是不會出現(xiàn)proxy失效的,例如:

const proxy = reactive({a:{a:1},b:{b:1}})
const {a,b} = proxy //a,b依然是響應式的

好了,為了解決第一種情況,toRefs出來了。

function toRefs(object) {
    //如果不是代理過的對象,不能使用toRefs
    if (!isProxy(object)) {
        console.warn(`toRefs() expects a reactive object but received a plain one.`);
    }
    //創(chuàng)建容器
    const ret = isArray(object) ? new Array(object.length) : {};
    //將解構后的值變?yōu)轫憫劫x值給ret容器
    for (const key in object) {
        toRef返回ObjectRefImpl實例返回一個對象
        ret[key] = toRef(object, key);
    }
    return ret;
}
//將代理的值變?yōu)閞ef
function toRef(object, key, defaultValue) {
    const val = object[key];
    return isRef(val)
        ? val
        : new ObjectRefImpl(object, key, defaultValue);
}
//ObjectRefImpl實例訪問value的時候相當于是
//訪問的proxy[key]這樣就依舊是響應式的
//同理設置的時候proxy[key] = xxx也是響應式的
//我們只需要訪問.value和設置.value就可以了
class ObjectRefImpl {
  constructor(_object, _key, _defaultValue) {
    //存儲proxy
    this._object = _object;
    //存儲key
    this._key = _key;
    this._defaultValue = _defaultValue;
    this.__v_isRef = true;//當前是ref
  }
  get value() {
    //this._object[this._key]相當于讀取了proxy中的值
    //會收集依賴
    const val = this._object[this._key];
    return val === undefined ? this._defaultValue : val;
  }
  set value(newVal) {
    //設置了proxy中的值觸發(fā)依賴更新
    this._object[this._key] = newVal;
  }
}

toRefs就是在解構之前,把要訪問的值變成一個對象,也就是說 {a} = toRefs(proxy) 中的a就是ObjectRefImpl實例,那么訪問 .value 就會去訪問 proxy[key] 這樣就可以收集依賴,set的時候就會觸發(fā)依賴。

(4).computed

這是一個計算屬性的api,我們可以通過訪問computed返回值的value屬性獲取最新的計算結果,并且computed返回值依然是響應式的,可以在effect中收集依賴,修改value屬性的時候能觸發(fā)依賴更新。

//對傳遞的參數(shù)進行整理生成ComputedRefImpl實例并返回
function computed(getterOrOptions, debugOptions, isSSR = false) {
    let getter;
    let setter;
    //第一個參數(shù)是函數(shù),則只有getter沒有setter
    const onlyGetter = shared.isFunction(getterOrOptions);
    if (onlyGetter) {
        getter = getterOrOptions;
        setter = () => {
           console.warn('Write operation failed: computed value is readonly');
        };
    }
    else {
        //獲取getter和setter
        //getter返回一個計算值
        //如果setter存在當修改ComputedRefImpl實例的value屬性
        //的時候會調(diào)用setter并把修改的值傳遞到setter中
        getter = getterOrOptions.get;
        setter = getterOrOptions.set;
    }
    //創(chuàng)建實例
    const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
    if (debugOptions && !isSSR) {
        cRef.effect.onTrack = debugOptions.onTrack;
        cRef.effect.onTrigger = debugOptions.onTrigger;
    }
    return cRef;
}
  • computed本身只是對傳遞的參數(shù)進行了整理,然后創(chuàng)建了ComputedRefImpl實例并且返回。
_a = "__v_isReadonly"
class ComputedRefImpl {
    constructor(getter, _setter, isReadonly, isSSR) {
        this._setter = _setter;
        this.dep = undefined;
        this.__v_isRef = true;
        this[_a] = false;
        this._dirty = true;
        //這里的邏輯reactivity上篇中已經(jīng)講過了
        this.effect = new ReactiveEffect(getter, () => {
            if (!this._dirty) {
                this._dirty = true;
                triggerRefValue(this);
            }
        });
        //在trigger中優(yōu)先觸發(fā)有computed屬性的effect
        this.effect.computed = this;
        this.effect.active = this._cacheable = !isSSR;
        this["__v_isReadonly"] = isReadonly;
    }
    get value() {
        const self = toRaw(this);
        trackRefValue(self);
        if (self._dirty || !self._cacheable) {
            self._dirty = false;
            self._value = self.effect.run();
        }
        return self._value;
    }
    set value(newValue) {
        this._setter(newValue);
    }
}

construtor中創(chuàng)建ReactiveEffect實例,第二個函數(shù)代表的是schduler調(diào)度器,如果有這個函數(shù),那么觸發(fā)依賴的時候?qū)⒉粫{(diào)用run方法而是調(diào)用schduler,所以如果調(diào)用這個函數(shù)表示computed中的getter中的某個代理屬性發(fā)生了改變.然后 _dirty = true 表示值發(fā)生了改變,那么ComputedRefImpl收集到的依賴將會被觸發(fā),同樣的ComputedRefImpl的依賴是在訪問ComputedRefImplvalue屬性的時候收集到的。

(5)其他api源碼

最后還有customRef以及deferredComputed大家看看源碼吧,不在進行講解了。

1.customRef的實現(xiàn)

//customRef的實現(xiàn)
function customRef(factory) {
    return new CustomRefImpl(factory);
}
class CustomRefImpl {
    constructor(factory) {
        this.dep = undefined;
        this.__v_isRef = true;
        const { get, set } = factory(
         () => trackRefValue(this), 
         () => triggerRefValue(this)
        );
        this._get = get;
        this._set = set;
    }
    get value() {
        return this._get();
    }
    set value(newVal) {
        this._set(newVal);
    }
}

2.deferredComputed的實現(xiàn)

function deferredComputed(getter) {
    return new DeferredComputedRefImpl(getter);
}
class DeferredComputedRefImpl {
 constructor(getter) {
  this.dep = undefined;
  this._dirty = true;
  this.__v_isRef = true;
  this[_a] = true;
  let compareTarget;
  let hasCompareTarget = false;
  let scheduled = false;
  this.effect = new ReactiveEffect(getter, (computedTrigger) => {
   if (this.dep) {
    if (computedTrigger) {
      compareTarget = this._value;
      hasCompareTarget = true;
    }
    else if (!scheduled) {
      const valueToCompare = hasCompareTarget ? compareTarget : this._value;
      scheduled = true;
      hasCompareTarget = false;
      scheduler(() => {
       if (this.effect.active && this._get() !== valueToCompare) {
           triggerRefValue(this);
       }
       scheduled = false;
      });
     }          
     for (const e of this.dep) {
       if (e.computed instanceof DeferredComputedRefImpl) {
         e.scheduler(true);
       }
     }
    }
    this._dirty = true;
  });
  this.effect.computed = this;
 }
 _get() {
   if (this._dirty) {
     this._dirty = false;
     return (this._value = this.effect.run());
   }
   return this._value;
 }
 get value() {
   trackRefValue(this);
   return toRaw(this)._get();
 }
}
const tick = Promise.resolve();
const queue = [];
let queued = false;
const scheduler = (fn) => {
    queue.push(fn);
    if (!queued) {
        queued = true;
        tick.then(flush);
    }
};
const flush = () => {
    for (let i = 0; i < queue.length; i++) {
        queue[i]();
    }
    queue.length = 0;
    queued = false;
};

最后總結:

好啦!恭喜你完成了整個reactivity的閱讀,相信你收獲頗豐。我們在第一部分手寫了簡單版的reactivity讓大家能夠迅速理解reactivity的核心實現(xiàn)便于大家能更快理解后面部分的源碼;在第二部分我們詳細講解了如何對數(shù)組和對象進行響應式處理;然后在第三部分我們詳細講解了對于set mapes6新出的結構進行攔截,與第二部分不同的是,集合類型的攔截是通過攔截各種操縱集合類型的api,然后實現(xiàn)的依賴收集和觸發(fā);最后一部分我們講解了ref computed toRefs的實現(xiàn),然后貼出了一些不常用的api的源碼。

以上就是Vue3源碼分析reactivity實現(xiàn)方法示例的詳細內(nèi)容,更多關于Vue3源碼分析reactivit方法的資料請關注腳本之家其它相關文章!

相關文章

最新評論