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

Vue3?計算屬性computed的實現(xiàn)原理

 更新時間:2022年08月08日 08:28:35   作者:紫圣  
這篇文章主要介紹了Vue3?計算屬性computed的實現(xiàn)原理,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下

版本:3.2.31

computed 的函數(shù)簽名

// packages/reactivity/src/computed.ts

// 只讀的
export function computed<T>(
  getter: ComputedGetter<T>,
  debugOptions?: DebuggerOptions
): ComputedRef<T>
// 可寫的 
export function computed<T>(
  options: WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions
): WritableComputedRef<T>
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions,
  isSSR = false
)

上面的代碼為 computed 的函數(shù)重載。在第一個重載中,接受一個 getter 函數(shù),并返回 ComputedRef 類型的值。也就是說,在這種情況下,computed 接受一個 getter 函數(shù),并根據(jù) getter 的返回值返回一個不可變的響應(yīng)式 ref 對象。

如下面的代碼所示:

const count = ref(1)
// computed 接受一個 getter 函數(shù)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 錯誤

在第二個重載中,computed 函數(shù)接受一個具有 get 和 set 函數(shù)的 options 對象,并返回一個可寫的 ref 對象。

如下面的代碼所示:

const count = ref(1)
const plusOne = computed({
  // computed 函數(shù)接受一個具有 get 和 set 函數(shù)的 options 對象
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

第三個重載是第一個重載和第二個重載的結(jié)合,此時 computed 函數(shù)既可以接受一個 getter 函數(shù),又可以接受一個具有 get 和 set 函數(shù)的 options 對象。

computed 的實現(xiàn)

// packages/reactivity/src/computed.ts

export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions,
  isSSR = false
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  // 判斷 getterOrOptions 參數(shù) 是否是一個函數(shù)
  const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    // getterOrOptions 是一個函數(shù),則將函數(shù)賦值給取值函數(shù)getter 
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
      : NOOP
  } else {
    // getterOrOptions 是一個 options 選項對象,分別取 get/set 賦值給取值函數(shù)getter和賦值函數(shù)setter
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  // 實例化一個 computed 實例
  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)

  if (__DEV__ && debugOptions && !isSSR) {
    cRef.effect.onTrack = debugOptions.onTrack
    cRef.effect.onTrigger = debugOptions.onTrigger
  }

  return cRef as any
}

在 computed 函數(shù)的實現(xiàn)中,首先判斷傳入的 getterOrOptions 參數(shù)是 getter 函數(shù)還是 options 對象。

如果 getterOrOptions 是 getter 函數(shù),則直接將傳入的參數(shù)賦值給 computed 的 getter 函數(shù)。由于這種情況下的計算屬性是只讀的,因此不允許設(shè)置 setter 函數(shù),并且在 DEV 環(huán)境中設(shè)置 setter 會報出警告。

如果 getterOrOptions 是 options 對象,則將該對象中的 get 、set 函數(shù)分別賦值給 computed 的 gettter 和 setter。

處理完 computed 的 getter 和 setter 后,則根據(jù) getter 和 setter 創(chuàng)建一個 ComputedRefImpl 類的實例,該實例是一個 ref 對象,最后將該 ref 對象返回。

下面我們來看看 ComputedRefImpl 這個類。

ComputedRefImpl 類

// packages/reactivity/src/computed.ts

export class ComputedRefImpl<T> {
  public dep?: Dep = undefined

  // value 用來緩存上一次計算的值
  private _value!: T
  public readonly effect: ReactiveEffect<T>

  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean

  // dirty標(biāo)志,用來表示是否需要重新計算值,為true 則意味著 臟, 需要計算
  public _dirty = true
  public _cacheable: boolean

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
    this.effect = new ReactiveEffect(getter, () => {
      // getter的時候,不派發(fā)通知
      if (!this._dirty) {
        this._dirty = true
        // 當(dāng)計算屬性依賴響應(yīng)式數(shù)據(jù)變化時,手動調(diào)用 triggerRefValue 函數(shù) 觸發(fā)響應(yīng)式
        triggerRefValue(this)
      }
    })
    this.effect.computed = this
    this.effect.active = this._cacheable = !isSSR
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    // 獲取原始對象
    const self = toRaw(this)
    // 當(dāng)讀取 value 時,手動調(diào)用 trackRefValue 函數(shù)進行追蹤
    trackRefValue(self)
    // 只有臟 才計算值,并將得到的值緩存到value中
    if (self._dirty || !self._cacheable) {
      // 將dirty設(shè)置為 false, 下一次訪問直接使用緩存的 value中的值
      self._dirty = false
      self._value = self.effect.run()!
    }
    // 返回最新的值
    return self._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}

緩存計算屬性,避免多次計算:

為了避免多次訪問計算屬性時導(dǎo)致副作用函數(shù)多次執(zhí)行,在 ComputedRefImpl 類中定義了一個私有變量 _value 和一個公共變量 _dirty。其中 _value 用來緩存上一次計算的值,_dirty 用來表示是否需要重新計算值,值為 true 時意味著「臟」, 則計算屬性需要重新計算。在讀取計算屬性時,會觸發(fā) getter 函數(shù),在 getter 函數(shù)中,判斷 _dirty 的值是否為 true,如果是,才重新執(zhí)行副作用,將執(zhí)行結(jié)果緩存到 _value 變量中,并返回最新的值。如果_dirty 的值為 false,說明計算屬性不需要重新計算,返回上一次計算的結(jié)果即可。

數(shù)據(jù)變化,計算屬性需重新計算:

當(dāng)計算屬性的依賴數(shù)據(jù)發(fā)生變化時,為了使得計算屬性是最新的,Vue 在 ComputedRefImpl 類的構(gòu)造函數(shù)中為 getter 創(chuàng)建了一個副作用函數(shù)。在該副作用函數(shù)中,判斷 this._dirty 標(biāo)記是否為 false,如果是,則將 this._dirty 置為 true,當(dāng)下一次訪問計算屬性時,就會重新執(zhí)行副作用函數(shù)計算值。

計算屬性中的 effect 嵌套:

當(dāng)我們在另一個 effect 中讀取計算屬性的值時,如下面代碼所示:

const sumResult = computed(() => obj.foo + obj.bar)

effect(() => {
  // 在該副作用函數(shù)中讀取 sumResult.value
  console.log(sumResult.value)
})

// 修改 obj.bar 的值
obj.bar++

如上面的代碼所示,sumResult 是一個計算屬性,并且在另一個 effect 的副作用函數(shù)中讀取了 sumResult.value 的值。如果此時修改了 obj.bar 的值,期望的結(jié)果是副作用函數(shù)重新執(zhí)行,但實際上并未重新觸發(fā)副作用函數(shù)執(zhí)行。

在一個 effect 中讀取計算屬性的值,其本質(zhì)上就是一個典型的 effect 嵌套。一個計算屬性內(nèi)部擁有自己的 effect ,并且它是懶執(zhí)行的,只有當(dāng)真正讀取計算屬性的值時才會執(zhí)行。當(dāng)把計算屬性用于另外一個 effect 時,就會發(fā)生 effect 嵌套,外層的 effect 不會被內(nèi)層 effect 中的響應(yīng)式數(shù)據(jù)收集。因此,當(dāng)讀取計算屬性的值時,需要手動調(diào)用 trackRefValue 函數(shù)進行追蹤,當(dāng)計算屬性依賴的響應(yīng)式數(shù)據(jù)發(fā)生變化時,手動調(diào)用 triggerRefValue 函數(shù)觸發(fā)響應(yīng)。

總結(jié)

computed 的實現(xiàn),它實際上就是一個懶執(zhí)行的副作用函數(shù),通過 _dirty 標(biāo)志使得副作用函數(shù)可以懶執(zhí)行。dirty 標(biāo)志用來表示是否需要重新計算值,當(dāng)值為 true 時意味著「臟」, 則計算屬性需要重新計算,即重新執(zhí)行副作用。

到此這篇關(guān)于Vue3 計算屬性computed的實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue3 computed實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 動態(tài)實現(xiàn)element ui的el-table某列數(shù)據(jù)不同樣式的示例

    動態(tài)實現(xiàn)element ui的el-table某列數(shù)據(jù)不同樣式的示例

    這篇文章主要介紹了動態(tài)實現(xiàn)element ui的el-table某列數(shù)據(jù)不同樣式的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 在vue中把含有html標(biāo)簽轉(zhuǎn)為html渲染頁面的實例

    在vue中把含有html標(biāo)簽轉(zhuǎn)為html渲染頁面的實例

    今天小編就為大家分享一篇在vue中把含有html標(biāo)簽轉(zhuǎn)為html渲染頁面的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-10-10
  • VUE項目初建和常見問題總結(jié)

    VUE項目初建和常見問題總結(jié)

    在本篇文章里小編給大家整理的是關(guān)于VUE 項目初建和常見問題以及相關(guān)知識點內(nèi)容,有需要的朋友們學(xué)習(xí)下。
    2019-09-09
  • ant-design-vue導(dǎo)航菜單a-menu的使用解讀

    ant-design-vue導(dǎo)航菜單a-menu的使用解讀

    這篇文章主要介紹了ant-design-vue導(dǎo)航菜單a-menu的使用解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue頁面切換項目實現(xiàn)轉(zhuǎn)場動畫的方法

    vue頁面切換項目實現(xiàn)轉(zhuǎn)場動畫的方法

    這篇文章主要介紹了vue頁面切換項目實現(xiàn)轉(zhuǎn)場動畫的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • vue實現(xiàn)簡單滑塊驗證

    vue實現(xiàn)簡單滑塊驗證

    這篇文章主要為大家詳細介紹了vue實現(xiàn)簡單滑塊驗證,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • vue實戰(zhàn)中的一些實用小魔法匯總

    vue實戰(zhàn)中的一些實用小魔法匯總

    這篇文章主要給大家介紹了關(guān)于vue實戰(zhàn)中一些實用小魔法的相關(guān)資料,這些技巧和竅門,可以幫助你成為更好的Vue開發(fā)人員,需要的朋友可以參考下
    2021-06-06
  • 微信小程序開發(fā)實現(xiàn)消息框彈出

    微信小程序開發(fā)實現(xiàn)消息框彈出

    在小程序的wxml文件中創(chuàng)建消息框,消息框一般包含要提示的消息內(nèi)容以及確認和取消按鈕,在小程序的wxss文件中定義消息框的樣式,在小程序的js文件中,我們需要通過Animation對象實現(xiàn)消息框的彈出動畫
    2023-12-12
  • 詳解Vue 動態(tài)組件與全局事件綁定總結(jié)

    詳解Vue 動態(tài)組件與全局事件綁定總結(jié)

    這篇文章主要介紹了詳解Vue 動態(tài)組件與全局事件綁定總結(jié),從示例中發(fā)現(xiàn)并解決問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • 詳解Vue如何自定義hooks(組合式)函數(shù)

    詳解Vue如何自定義hooks(組合式)函數(shù)

    這篇文章主要為大家詳細介紹了在Vue中如何實現(xiàn)自定義hooks(組合式)函數(shù),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-03-03

最新評論