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

vue3中effect函數(shù)到底是什么詳解

 更新時(shí)間:2023年06月07日 10:57:51   作者:木木夏呀  
Effect幾乎是Vue3.0中最重要的一個(gè)功能了,計(jì)算屬性監(jiān)聽函數(shù)都是基于effect實(shí)現(xiàn)的,下面這篇文章主要給大家介紹了關(guān)于vue3中effect函數(shù)到底是什么的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

vue3中的effect函數(shù)是響應(yīng)式的核心,它被稱作副作用函數(shù),那么什么是副作用函數(shù)呢?我們來看一下。

vue中,數(shù)據(jù)target改變,可能會(huì)引起其他數(shù)據(jù)或者視圖發(fā)生變化,那么,其他數(shù)據(jù)改變以及視圖改變這些效果就是target改變的副作用。像watch、computed、這些都是會(huì)產(chǎn)生副作用的函數(shù),它們的底層都是使用了effect。

我們先來看看effect的原理。首先先來認(rèn)識(shí)幾個(gè)重要的全局變量。

targetMap

targetMap是一個(gè)WeakMap,存儲(chǔ)了{(lán)target -> key -> dep}的關(guān)系。targetMap的key是需要做響應(yīng)式處理的原始對(duì)象,targetMap的value是一個(gè)Map,Map的key是原始對(duì)象的屬性,Map的value是每個(gè)屬性關(guān)聯(lián)的副作用函數(shù)Set。副作用函數(shù)就是我們需要追蹤的依賴,也就是訂閱者。

activeEffect

activeEffect保存當(dāng)前正在執(zhí)行的副作用函數(shù),它是一個(gè)對(duì)象,effect的類型如下:

let activeEffect: ReactiveEffect | undefined

// effect類型
class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = []
  parent: ReactiveEffect | undefined = undefined
  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope
  ) {
    recordEffectScope(this, scope)
  }
  
  run(){}
  stop() {}

}

shouldTrack

shouldTrack 變量用來標(biāo)識(shí)是否開啟依賴搜集,只有 shouldTrack 的值為 true 時(shí),才進(jìn)行依賴收集,即將副作用函數(shù)添加到依賴集合中。初始化值是true。當(dāng)執(zhí)行run()時(shí),會(huì)將shouldTrack設(shè)置為true,開啟依賴收集。

effect

接下來看看副作用函數(shù)effect的實(shí)現(xiàn)原理。當(dāng)依賴的數(shù)據(jù)變化的時(shí)候副作用函數(shù)便會(huì)觸發(fā),想當(dāng)然的肯定會(huì)涉及到依賴收集收集與追蹤,因此之后我們還會(huì)講解一下track、trigger與effect的關(guān)系。

interface ReactiveEffectRunner<T = any> {
  (): T
  // ReactiveEffect就是上面提到的activeEffect的類型
  effect: ReactiveEffect
}

function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions
): ReactiveEffectRunner {

  // 如果傳入的fn本身就是effect,那么就直接執(zhí)行 effect的副作用
  if ((fn as ReactiveEffectRunner).effect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }

  //  將fn包裝成effect  
  const _effect = new ReactiveEffect(fn)
  if (options) {
    extend(_effect, options)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  
  // 不是延遲執(zhí)行,直接執(zhí)行副作用函數(shù)
  if (!options || !options.lazy) {
    _effect.run()
  }
  
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}

fn就是傳給effect的副作用,將fn傳給ReactiveEffect包裝一下,將其包裝成標(biāo)準(zhǔn)的effect類型。標(biāo)準(zhǔn)的effect類型是ReactiveEffect類,具有active、deps、deps、run()、stop()這些屬性方法。其中run方法中就是執(zhí)行傳入的副作用函數(shù)fn。 但是effect函數(shù)返回的不是ReactiveEffect類型,而是ReactiveEffectRunner類型,接口ReactiveEffectRunner中具有effect屬性,它是ReactiveEffect類型,因此effect最終需要runner.effect = _effect,然后返回runner。
我們傳入的副作用在effect的run()中執(zhí)行。需要重點(diǎn)看一下run。

  run() {
    if (!this.active) {
      return this.fn()
    }
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      this.parent = activeEffect
      // 全局activeEffect指向當(dāng)前執(zhí)行的自身
      activeEffect = this
      // 開啟依賴收集
      shouldTrack = true
      trackOpBit = 1 << ++effectTrackDepth
      if (effectTrackDepth <= maxMarkerBits) {
        initDepMarkers(this)
      } else {
        cleanupEffect(this)
      }
      // 執(zhí)行副作用函數(shù)
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        finalizeDepMarkers(this)
      }
      trackOpBit = 1 << --effectTrackDepth
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined
      if (this.deferStop) {
        this.stop()
      }
    }
  }

執(zhí)行run的時(shí)候,將全局activeEffect指向自身,也就是當(dāng)前執(zhí)行的effect,然后開啟依賴收集標(biāo)識(shí)位,執(zhí)行副作用函數(shù)。

說到這,我們好像還沒發(fā)現(xiàn)effect與tracktrigger有什么關(guān)系。track、trigger函數(shù)與effect函數(shù)都是位于源碼同一個(gè)文件下的,那么它們肯定是有關(guān)聯(lián)的。接下來,我們看看track的邏輯。

track(target: object, type: TrackOpTypes, key: unknown) {
  if (shouldTrack && activeEffect) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = createDep()))
    }
    const eventInfo = __DEV__
      ? { effect: activeEffect, target, type, key }
      : undefined
    trackEffects(dep, eventInfo)
  }
}

我們知道,在研究vue的雙向數(shù)據(jù)綁定時(shí),需要進(jìn)行依賴收集與依賴追蹤。track就是依賴收集的過程,當(dāng)我們通過響應(yīng)式API初始化數(shù)據(jù)或者模板中渲染一些變量的時(shí)候,就需要進(jìn)行依賴收集,也就是track。依賴,也就是我們的副作用函數(shù),依賴收集的過程其實(shí)就是收集副作用函數(shù)的過程。而副作用,就是我們剛剛上文講的effect。這就關(guān)聯(lián)上了。 剛剛研究effect的時(shí)候,提到了一些全局變量,其中targetMap存儲(chǔ)了{target -> key -> dep}的關(guān)系,我們依賴收集的過程,就是將副作用存儲(chǔ)起來的過程。因此在track中,傳入的參數(shù)target對(duì)應(yīng)的就是targetMap的key。先查看全局變量targetMap中是否存在該target,沒有的話就初始化map賦值。同理,通過key來尋找最終的副作用Set,沒有的話就初始化設(shè)置。至此,我們完善了{target -> key -> dep}的關(guān)系,接下來看看 trackEffects。

export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  let shouldTrack = false
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      dep.n |= trackOpBit // set newly tracked
      shouldTrack = !wasTracked(dep)
    }
  } else {
    // Full cleanup mode.
    shouldTrack = !dep.has(activeEffect!)
  }
  if (shouldTrack) {
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack({
        effect: activeEffect!,
        ...debuggerEventExtraInfo!
      })
    }
  }
}

trackEffects的邏輯其實(shí)就是將當(dāng)前激活的activeEffect放到dep中,這樣就完成了依賴收集過程。 trigger過程就不多贅述了,就是根據(jù){target -> key -> dep}的關(guān)系一層一層的找到最后的dep,然后執(zhí)行其中的副作用函數(shù)即可。
說到這,可能還是有人不明白,如果不使用watch、computed這些帶有副作用的 api, track 、triggereffect又有什么關(guān)系呢?那track收集的副作用又是什么呢?

說到這里我想說,關(guān)于副作用,除了watch、computed這些api中用戶手動(dòng)傳入的fn算作副作用,頁(yè)面渲染也是副作用。我們使用vue中雙向數(shù)據(jù)綁定的特性,意味著當(dāng)我們定義一個(gè)響應(yīng)式變量,如果模板中使用了這個(gè)響應(yīng)式變量,那么這個(gè)變量在模板中的渲染是響應(yīng)式的,這個(gè)渲染是副作用,那么這個(gè)頁(yè)面渲染就需要被收集到dep中。如果這個(gè)變量還是作為 watch中fn的內(nèi)部出現(xiàn)的,那么這個(gè)變量的副作用又多了一個(gè),還需要被額外收集。所以dep是一個(gè)Set的數(shù)據(jù)結(jié)構(gòu)。當(dāng)這個(gè)變量變化的時(shí)候,頁(yè)面要重新渲染,watch也要重新計(jì)算,這就是trigger的過程。

當(dāng)然這些針對(duì)的是響應(yīng)式數(shù)據(jù),如果我們僅僅定義一個(gè)靜態(tài)數(shù)據(jù),那么是不需要進(jìn)行依賴收集的,它也不是響應(yīng)式的。 說到這里應(yīng)該可以明白,effect與響應(yīng)式密切相關(guān)了。

computed

講完effect,我們不難知道computed、watch也是一種effect,只是參數(shù)不同而已。我們簡(jiǎn)單看一下computed。

constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
  // computed也是實(shí)例化effect
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this)
      }
    })
    this.effect.computed = this
    this.effect.active = this._cacheable = !isSSR
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

從computed的構(gòu)造器中,我們可以看到computed也是實(shí)例化effect實(shí)現(xiàn)的,只是傳的參數(shù)不同而已,我們暫且稱作computedEffect??梢钥吹絚omputedEffect的computed屬性是true。

watch

注意:watch雖然依賴于effect函數(shù),但是在源碼目錄中卻不屬于reactivity目錄,而是runtime-core目錄下的。

  const effect = new ReactiveEffect(getter, scheduler)

可以看到watch中同樣使用ReactiveEffect創(chuàng)建副作用,只不過多傳了scheduler參數(shù)。

總結(jié)

到此這篇關(guān)于vue3中effect函數(shù)到底是什么的文章就介紹到這了,更多相關(guān)vue3中effect函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue+elementui(對(duì)話框中form表單的reset問題)

    vue+elementui(對(duì)話框中form表單的reset問題)

    這篇文章主要介紹了vue+elementui(對(duì)話框中form表單的reset問題),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 詳解vue3中組件的非兼容變更

    詳解vue3中組件的非兼容變更

    這篇文章主要介紹了詳解vue3中組件的非兼容變更,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-03-03
  • Vue集成three.js并加載glb、gltf、FBX、json模型的場(chǎng)景分析

    Vue集成three.js并加載glb、gltf、FBX、json模型的場(chǎng)景分析

    這篇文章主要介紹了Vue集成three.js,并加載glb、gltf、FBX、json模型,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • 基于Vue3實(shí)現(xiàn)一個(gè)小相冊(cè)詳解

    基于Vue3實(shí)現(xiàn)一個(gè)小相冊(cè)詳解

    這篇文章主要為大家詳細(xì)介紹了如何基于Vue3實(shí)現(xiàn)一個(gè)小相冊(cè)效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • vue 使用 canvas 實(shí)現(xiàn)手寫電子簽名

    vue 使用 canvas 實(shí)現(xiàn)手寫電子簽名

    這篇文章主要介紹了vue 使用 canvas 實(shí)現(xiàn)手寫電子簽名功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Vue.extend和VueComponent的關(guān)系源碼解析

    Vue.extend和VueComponent的關(guān)系源碼解析

    這篇文章主要為大家詳解了Vue.extend和VueComponent的關(guān)系源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • django+vue實(shí)現(xiàn)注冊(cè)登錄的示例代碼

    django+vue實(shí)現(xiàn)注冊(cè)登錄的示例代碼

    這篇文章主要介紹了django+vue實(shí)現(xiàn)注冊(cè)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 關(guān)于axios配置多個(gè)請(qǐng)求地址(打包后可通過配置文件修改)

    關(guān)于axios配置多個(gè)請(qǐng)求地址(打包后可通過配置文件修改)

    這篇文章主要介紹了關(guān)于axios配置多個(gè)請(qǐng)求地址(打包后可通過配置文件修改),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 使用kbone解決Vue項(xiàng)目同時(shí)支持小程序問題

    使用kbone解決Vue項(xiàng)目同時(shí)支持小程序問題

    這篇文章主要介紹了使用kbone解決Vue項(xiàng)目同時(shí)支持小程序問題,本文通過一個(gè)todo的例子跟大家詳細(xì)介紹,需要的朋友可以參考下
    2019-11-11
  • 詳解Electron中如何使用SQLite存儲(chǔ)筆記

    詳解Electron中如何使用SQLite存儲(chǔ)筆記

    這篇文章主要為大家介紹了Electron中如何使用SQLite存儲(chǔ)筆記示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論