最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理

 更新時(shí)間:2023年03月29日 09:16:47   作者:PHM  
這篇文章主要為大家介紹了Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

vue3新增effectScope相關(guān)的API

其官方的描述是創(chuàng)建一個(gè) effect 作用域,可以捕獲其中所創(chuàng)建的響應(yīng)式副作用 (即計(jì)算屬性和偵聽器),這樣捕獲到的副作用可以一起處理。并給出了示例:

const scope = effectScope()
scope.run(() => {
  const doubled = computed(() => counter.value * 2)
  watch(doubled, () => console.log(doubled.value))
  watchEffect(() => console.log('Count: ', doubled.value))
})
// 處理掉當(dāng)前作用域內(nèi)的所有 effect
scope.stop()

我們就從這個(gè)示例入手看看具體的源碼實(shí)現(xiàn):

effectScope

// packages/reactivity/src/effectScope.ts
export function effectScope(detached?: boolean) {
  // 返回EffectScope實(shí)例
  return new EffectScope(detached)
}

EffectScope

export class EffectScope {
  /**
   * @internal
   */
  private _active = true
  /**
   * @internal
   */
  effects: ReactiveEffect[] = []
  /**
   * @internal
   */
  cleanups: (() => void)[] = []
  /**
   * only assigned by undetached scope
   * @internal
   */
  parent: EffectScope | undefined
  /**
   * record undetached scopes
   * @internal
   */
  scopes: EffectScope[] | undefined
  /**
   * track a child scope's index in its parent's scopes array for optimized
   * // index作用:在父作用域數(shù)組中跟蹤子作用域范圍索引以進(jìn)行優(yōu)化。
   * removal
   * @internal
   */
  private index: number | undefined
  constructor(public detached = false) {
    // 記錄當(dāng)前scope為parent scope
    this.parent = activeEffectScope
    if (!detached && activeEffectScope) {
      this.index =
        (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
          this
        ) - 1
    }
  }
  get active() {
    return this._active
  }
  run<T>(fn: () => T): T | undefined {
    if (this._active) {
      const currentEffectScope = activeEffectScope
      try {
        activeEffectScope = this
        return fn()
      } finally {
        activeEffectScope = currentEffectScope
      }
    } else if (__DEV__) {
      warn(`cannot run an inactive effect scope.`)
    }
  }
  /**
   * This should only be called on non-detached scopes
   * 必須在非分離的作用域上調(diào)用
   * @internal
   */
  on() {
    activeEffectScope = this
  }
  /**
   * This should only be called on non-detached scopes
   * @internal
   */
  off() {
    activeEffectScope = this.parent
  }
  // stop方法
  stop(fromParent?: boolean) {
    if (this._active) {
      let i, l
      // stop effects
      for (i = 0, l = this.effects.length; i < l; i++) {
        this.effects[i].stop()
      }
      // 執(zhí)行所有的cleanups
      for (i = 0, l = this.cleanups.length; i < l; i++) {
        this.cleanups[i]()
      }
      // 遞歸停止所有的子作用域
      if (this.scopes) {
        for (i = 0, l = this.scopes.length; i < l; i++) {
          this.scopes[i].stop(true)
        }
      }
      // nested scope, dereference from parent to avoid memory leaks
      if (!this.detached && this.parent && !fromParent) {
        // optimized O(1) removal
        const last = this.parent.scopes!.pop()
        if (last && last !== this) {
          this.parent.scopes![this.index!] = last
          last.index = this.index!
        }
      }
      this.parent = undefined
      this._active = false
    }
  }
}

在執(zhí)行scope.run的時(shí)候會(huì)將this賦值到全局的activeEffectScope變量,然后執(zhí)行傳入函數(shù)。對(duì)于computed、watch、watchEffect(watchEffect是調(diào)用doWatch實(shí)現(xiàn)的,與watch實(shí)現(xiàn)響應(yīng)式綁定的方式相同)這些API都會(huì)創(chuàng)建ReactiveEffect實(shí)例來建立響應(yīng)式關(guān)系,而收集對(duì)應(yīng)的響應(yīng)式副作用就發(fā)生在ReactiveEffect創(chuàng)建的時(shí)候,我們來看一下ReactiveEffect的構(gòu)造函數(shù):

// ReactiveEffect的構(gòu)造函數(shù)
constructor(
  public fn: () => T,
  public scheduler: EffectScheduler | null = null,
  scope?: EffectScope
) {
  // effect實(shí)例默認(rèn)會(huì)被記錄到指定scope中
  // 如果沒有指定scope則會(huì)記錄到全局activeEffectScope中
  recordEffectScope(this, scope)
}
// recordEffectScope實(shí)現(xiàn)
export function recordEffectScope(
  effect: ReactiveEffect,
  // scope默認(rèn)值為activeEffectScope
  scope: EffectScope | undefined = activeEffectScope
) {
  if (scope && scope.active) {
    scope.effects.push(effect)
  }
}

可以看到如果我們沒有傳入scope參數(shù),那么在執(zhí)行recordEffectScope時(shí)就會(huì)有一個(gè)默認(rèn)的參數(shù)為activeEffectScope,這個(gè)值不正是我們scope.run的時(shí)候賦值的嗎!所以新創(chuàng)建的effect會(huì)被放到activeEffectScope.effects中,這就是響應(yīng)式副作用的收集過程。
那么對(duì)于一起處理就比較簡(jiǎn)單了,只需要處理scope.effects即可

組件的scope

日常開發(fā)中其實(shí)并不需要我們關(guān)心組件副作用的收集和清除,因?yàn)檫@些操作是已經(jīng)內(nèi)置好的,我們來看一下源碼中是怎么做的

組件實(shí)例中的scope

在組件實(shí)例創(chuàng)建的時(shí)候就已經(jīng)new了一個(gè)屬于自已的scope對(duì)象了:

const instance: ComponentInternalInstance = {
  ...
  // 初始化scope
  scope: new EffectScope(true /* detached */),
  ...
}

在我們執(zhí)行setup之前,會(huì)調(diào)用setCurrentInstance,他會(huì)調(diào)用instance.scope.on,那么就會(huì)將activeEffectScope賦值為instance.scope,那么在setup中注冊(cè)的computed、watch等就都會(huì)被收集到instance.scope.effects

function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  // 組件對(duì)象
  const Component = instance.type as ComponentOptions
  ...
  // 2. call setup()
  const { setup } = Component
  if (setup) {
    // 創(chuàng)建setupContext
    const setupContext = (instance.setupContext =
      // setup參數(shù)個(gè)數(shù)判斷 大于一個(gè)參數(shù)創(chuàng)建setupContext
      setup.length > 1 ? createSetupContext(instance) : null)
    // instance賦值給currentInstance
    // 設(shè)置當(dāng)前實(shí)例為instance 為了在setup中可以通過getCurrentInstance獲取到當(dāng)前實(shí)例
    // 同時(shí)開啟instance.scope.on()
    setCurrentInstance(instance)
    // 暫停tracking 暫停收集副作用函數(shù)
    pauseTracking()
    // 執(zhí)行setup
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      // setup參數(shù)
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    // 重新開啟副作用收集
    resetTracking()
    // currentInstance置為空 
    // activeEffectScope賦值為instance.scope.parent
    // 同時(shí)instance.scope.off()
    unsetCurrentInstance()
    ...
  } else {
    finishComponentSetup(instance, isSSR)
  }
}

對(duì)于選項(xiàng)式API的收集是同樣的操作:

// support for 2.x options
if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
  setCurrentInstance(instance)
  pauseTracking()
  // 處理options API
  applyOptions(instance)
  resetTracking()
  unsetCurrentInstance()
}

完成了收集那么對(duì)于清理就只需要在組件卸載的時(shí)候執(zhí)行stop方法即可:

// packages/runtime-core/src/renderer.ts
const unmountComponent = (
  instance: ComponentInternalInstance,
  parentSuspense: SuspenseBoundary | null,
  doRemove?: boolean
) => {
  if (__DEV__ && instance.type.__hmrId) {
    unregisterHMR(instance)
  }
  const { bum, scope, update, subTree, um } = instance
  ...
  // stop effects in component scope
  // 副作用清除
  scope.stop()
  ...
}

以上就是Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Vue3 effectScope API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解SpringBoot 解決攔截器注入Service為空問題

    詳解SpringBoot 解決攔截器注入Service為空問題

    這篇文章主要介紹了詳解SpringBoot 解決攔截器注入Service為空問題的解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • 使用Spring自定義實(shí)現(xiàn)IOC和依賴注入(注解方式)

    使用Spring自定義實(shí)現(xiàn)IOC和依賴注入(注解方式)

    這篇文章主要介紹了使用Spring自定義實(shí)現(xiàn)IOC和依賴注入(注解方式),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java中反射的學(xué)習(xí)筆記分享

    Java中反射的學(xué)習(xí)筆記分享

    反射是Java編程語言中的一個(gè)特性。它允許執(zhí)行的Java程序檢查或?操作?自身,并操作程序的內(nèi)部屬性。本文將通過幾個(gè)示例帶大家詳細(xì)了解一下Java中反射的使用,需要的可以參考一下
    2022-11-11
  • springboot多模塊中的共用配置文件詳解

    springboot多模塊中的共用配置文件詳解

    這篇文章主要介紹了springboot多模塊中的共用配置文件詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 如何在IDEA中快速解決Jar沖突詳解

    如何在IDEA中快速解決Jar沖突詳解

    相信很多同學(xué)在過去做項(xiàng)目都遇到過Jar沖突的問題,在本地環(huán)境沒問題,一旦部署到測(cè)試或生產(chǎn)環(huán)境突然就啟動(dòng)報(bào)錯(cuò),報(bào)類似classNotFound的Exception,本文詳細(xì)整理了如何在IDEA中快速解決Jar沖突,需要的朋友可以參考下
    2021-06-06
  • Java異常處理之java.lang.ClassCastException問題

    Java異常處理之java.lang.ClassCastException問題

    這篇文章主要介紹了Java異常處理之java.lang.ClassCastException問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)之雙向鏈表

    Java數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)之雙向鏈表

    雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個(gè)數(shù)據(jù)結(jié)點(diǎn)中都有兩個(gè)指針,分別指向直接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個(gè)結(jié)點(diǎn)開始,都可以很方便地訪問它的前驅(qū)結(jié)點(diǎn)和后繼結(jié)點(diǎn)。本文將為大家詳細(xì)介紹雙向鏈表的特點(diǎn)與使用,需要的可以參考一下
    2021-12-12
  • 深入理解Java中的Lambda表達(dá)式

    深入理解Java中的Lambda表達(dá)式

    這篇文章主要介紹了深入理解Java中的Lambda表達(dá)式,Lambda在各編程語言中都是非常重要的特性,而Java中則加入得有些太晚...需要的朋友可以參考下
    2015-07-07
  • Java中jqGrid 學(xué)習(xí)筆記整理——進(jìn)階篇(二)

    Java中jqGrid 學(xué)習(xí)筆記整理——進(jìn)階篇(二)

    這篇文章主要介紹了Java中jqGrid 學(xué)習(xí)筆記整理——進(jìn)階篇(二)的相關(guān)資料,需要的朋友可以參考下
    2016-04-04
  • java設(shè)計(jì)模式之簡(jiǎn)單工廠模式簡(jiǎn)述

    java設(shè)計(jì)模式之簡(jiǎn)單工廠模式簡(jiǎn)述

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之簡(jiǎn)單工廠模式,簡(jiǎn)單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例,感興趣的小伙伴們可以參考一下
    2016-08-08

最新評(píng)論

邵阳县| 昌都县| 禄丰县| 江永县| 南乐县| 和静县| 宁波市| 穆棱市| 蓬莱市| 奉贤区| 清河县| 阿坝| 尼木县| 定结县| 图木舒克市| 尚志市| 兴城市| 敦煌市| 曲阜市| 汤阴县| 江华| 蒙阴县| 枣阳市| 奇台县| 张掖市| 广州市| 延庆县| 天台县| 合川市| 板桥市| 正镶白旗| 天峨县| 宣武区| 尚志市| 临海市| 陇西县| 平陆县| 通州市| 桐乡市| 鹤庆县| 汉源县|