vue3響應式數據最全最細緻解析,vue3源碼解析持續更新中

本文是我新開的坑的第一篇文章,這個坑就是vue3,接下來我會圍繞着vue3進行一系列的動做,包括但不限於:html

  • 完整的源碼解析
  • jsx工程最佳實踐
  • 個性化的jsx方案
  • vue3生態庫開發(目前有一個正在進行)
  • 以及可能的自定義一個vue3的runtime

關於源碼解析,網站已經上線,vue3源碼解析,最佳實踐,網站是逐行代碼形式的解析,更多關注於源碼,而在掘金上分享的文章則相似於總結,會用更復合一篇文章的結構來寫。若是你想持續跟進vue3源碼,能夠打開前面的網站關注我。vue

那麼,開始!react

vue3最大的變化莫過於其對於響應式原理的重構,以及其新發布的composition api,本文聚焦於前者,來深度剖析一下vue3中響應式究竟是怎麼實現的。api

咱們以reactiveAPI 爲例,數組

const Comp = {
    setup() {
        const state = reactive({
            a: 'jokcy'
        })
        
        return () => {
            return <input value={state.a} onChange={(e) => state.a = e.targent.value} /> } } } 複製代碼

咱們看上面的例子,這個例子很簡單,建立了一個組件,他有一個響應式的數據對象,而後render裏面的input的value綁定的是state.a以及他的onChange會修改state.a。這是很是簡單且直觀的一個數據綁定的例子,而這個邏輯能實現的根本緣由,是咱們在調用state.a = xxx的時候,vue會從新渲染咱們return的render函數,來更新節點函數

而篇文章就是要來看一下,咱們經過reactive建立的對象,到底有什麼魔力,可以幫咱們完成這個任務。網站

其實自己 API 是很簡單的,傳入一個對象,返回一個 reactive 對象,建立的方法是createReactiveObjectui

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  );
}
複製代碼
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any> ) {
  // 前面都是一些對象是否已經proxy等的判斷邏輯,這裏就不展現了

  const observed = new Proxy(
    target,
    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
  );
  def(
    target,
    isReadonly ? ReactiveFlags.READONLY : ReactiveFlags.REACTIVE,
    observed
  );
  return observed;
}
複製代碼

那麼這裏最重要的就是new Proxy了,能夠說理解 vue3 的響應式原理過程就是理解這個proxy建立的過程,而瞭解這個過程,主要就是看第二個參數,在這裏就是collectionHandlers或者baseHandlers,大部分是後者,前者主要針對,Set、Map 等。spa

那麼咱們就來看baseHandlers代理

export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys,
};
複製代碼

可見 vue 代理了這幾個操做,那麼咱們一個個看這幾個操做作了啥:

function get(target: object, key: string | symbol, receiver: object) {
  // ...內部key的貨足

  // 關於數組的一些特殊處理
  const targetIsArray = isArray(target);
  if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
    return Reflect.get(arrayInstrumentations, key, receiver);
  }

  // 獲取請求值
  const res = Reflect.get(target, key, receiver);

  // ...若是是內部值的獲取則直接返回res

  if (!isReadonly) {
    track(target, TrackOpTypes.GET, key);
  }

  // 返回的一些處理

  return res;
}
複製代碼

這裏的重點就在於track(target, TrackOpTypes.GET, key);,這個是咱們正常獲取值的時候都會執行到的代碼:

export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (!shouldTrack || activeEffect === undefined) {
    return;
  }
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect);
    activeEffect.deps.push(dep);
    if (__DEV__ && activeEffect.options.onTrack) {
      activeEffect.options.onTrack({
        effect: activeEffect,
        target,
        type,
        key,
      });
    }
  }
}
複製代碼

好了,重點來了,咱們逐行分析。

第一個if,就是根據環境變量的shouldTrack來判斷是否要進行跟蹤,若是你已經看過個人源碼解析中的processComponent的章節,那麼你如今應該就是豁然開朗的感受。由於在執行processComponent裏面的setup的時候,咱們特意關閉了track,而那時候就是把shouldTrack改成了false

let depsMap = targetMap.get(target)這行,targetMap是一個 map,用來記錄全部的響應對象。以後若是目前沒有記錄該對象,那麼就從新記錄。

這個 target 對應的也是一個 map,他會對每一個 key 創建一個 set。

最後要記錄此次的 effect 了,這裏所謂的effect是什麼呢?就是當前正在調用這個對象的函數。在咱們的例子裏面,就是return回去的render函數,這個函數在執行的時候會調用state.a因此會進入proxy對於get的代理,這個時候 proxy 就調用了track,那麼這時候的activeEffect就是這個 render 方法。這裏的effect就是,當state.a改動的時候,咱們須要從新執行該 render 方法來進行渲染。

那麼他是何時被設置的呢?在mount章節的時候咱們提到了,在執行render方法的時候,咱們執行這樣一句代碼instance.update = effect(function componentEffect()...),就是在這裏調用的effect方法裏面,把activeEffect記錄爲componentEffect,而這個componentEffect裏面則運行了render方法。

另外這個getHandler裏面有句代碼也挺有意思的:

if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}
複製代碼

在獲取屬性的時候,若是返回的值是對象的時候,纔會對其執行reactive,這也就是延遲處理,並且readonly的話是不執行reactive的。

OK,到這裏咱們已經知道了在執行render函數的時候,由於咱們調用了state.a因此這個函數就至關於依賴state.a,這在vue3裏面被稱爲effect

那麼下一篇文章,咱們就來說一講,這些effectstate.a變更的時候是如何被調用的,敬請期待。

相關文章
相關標籤/搜索