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

Vue3計算屬性和異步計算屬性方式

 更新時間:2022年09月07日 11:08:52   作者:SunsetFeng  
這篇文章主要介紹了Vue3計算屬性和異步計算屬性方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一、簡要介紹

不論是計算屬性,還是異步計算屬性,都是依托于Vue3整體的響應式原理實現的。其核心依舊是ReacetEffect類。如果對響應式原理不清楚,建議先看響應式原理章節(jié)。

計算屬性和常規(guī)的動態(tài)響應區(qū)別在于它不會主動的去執(zhí)行ReacteEffect所關聯的回調方法,而是用一個標記來表示當前的值是否有改變,如果有改變,則重新調用回調方法獲取,如果沒改動,則直接獲取上次計算的值。

二、計算屬性核心源碼 

export type ComputedGetter<T> = (...args: any[]) => T
export type ComputedSetter<T> = (v: T) => void
export interface WritableComputedOptions<T> {
  get: ComputedGetter<T>
  set: ComputedSetter<T>
}
class ComputedRefImpl<T> {
  public dep?: Dep = undefined
  private _value!: T
  private _dirty = true
  public readonly effect: ReactiveEffect<T>
  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean
  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    //內部存儲一個ReactiveEffect對象
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        //標記下次讀取將重新計算值
        this._dirty = true
        //觸發(fā)依賴更新
        triggerRefValue(this)
      }
    })
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }
  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    //收集依賴
    trackRefValue(self)
    //是否重新計算標記
    if (self._dirty) {
      //重新計算
      self._dirty = false
      self._value = self.effect.run()!
    }
    //直接獲取計算好的值
    return self._value
  }
  set value(newValue: T) {
    this._setter(newValue)
  }
}
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  const onlyGetter = isFunction(getterOrOptions)
  if (onlyGetter) {
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
      : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  //只需要關注這兒
  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)
  if (__DEV__ && debugOptions) {
    cRef.effect.onTrack = debugOptions.onTrack
    cRef.effect.onTrigger = debugOptions.onTrigger
  }
  return cRef as any
}

一個計算屬性對象的生成都是通過computed方法生成的,這個方法其實就是接收一個get方法和一個set方法,并生成一個ComputedRefImpl類型的對象。ComputedRefImpl類的實現很簡單,生成一個ReactiveEffect類型對象,并實現一個value屬性的讀寫方法。

在讀的時候收集依賴,并判斷是否重新計算。特殊的地方在于這個ReactiveEffect類型的對象接收了第二個參數。

我們細看一下觸發(fā)依賴更新的代碼,如下:

export function triggerEffects(
? dep: Dep | ReactiveEffect[],
? debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
? // spread into array for stabilization
? for (const effect of isArray(dep) ? dep : [...dep]) {
? ? if (effect !== activeEffect || effect.allowRecurse) {
? ? ? if (__DEV__ && effect.onTrigger) {
? ? ? ? effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
? ? ? }
? ? ? if (effect.scheduler) {
? ? ? ? effect.scheduler()
? ? ? } else {
? ? ? ? effect.run()
? ? ? }
? ? }
? }
}

里面邏輯很簡單,遍歷依賴里面的ReactiveEffect類型對象,如果存在scheduler方法,就調用這個方法。我們在看ReactiveEffect類的定義,代碼如下:

export class ReactiveEffect<T = any> {
? active = true
? deps: Dep[] = []
? // can be attached after creation
? computed?: boolean
? allowRecurse?: boolean
? onStop?: () => void
? // dev only
? onTrack?: (event: DebuggerEvent) => void
? // dev only
? onTrigger?: (event: DebuggerEvent) => void
? constructor(
? ? public fn: () => T,
? ? public scheduler: EffectScheduler | null = null,
? ? scope?: EffectScope | null
? ) {
? ? recordEffectScope(this, scope)
? }
}

此時就可以發(fā)現,ComputedRefImpl類里面的effect對象接收的第二個參數就是scheduler方法,因此當有依賴的數據變更時,會執(zhí)行這個方法,這個方法很好理解。

就是改變標記,意味著下一次讀取這個計算屬性,你需要重新計算了,不能用之前的緩存值了。

接著觸發(fā)依賴更新,不用疑惑,因為這個計算屬性本身也是響應式的,自身改變需要通知相應的依賴更新。至于這個判斷,是百分之百為true的,因為觸發(fā)依賴需要先添加依賴,而在讀取value值添加依賴會將標志置為false。

三、異步計算屬性核心源碼

const tick = Promise.resolve()
const queue: any[] = []
let queued = false
const scheduler = (fn: any) => {
? 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
}
class DeferredComputedRefImpl<T> {
? public dep?: Dep = undefined
? private _value!: T
? private _dirty = true
? public readonly effect: ReactiveEffect<T>
? public readonly __v_isRef = true
? public readonly [ReactiveFlags.IS_READONLY] = true
? constructor(getter: ComputedGetter<T>) {
? ? let compareTarget: any
? ? let hasCompareTarget = false
? ? let scheduled = false
? ? this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => {
? ? ? if (this.dep) {
? ? ? ? if (computedTrigger) {
? ? ? ? ? compareTarget = this._value
? ? ? ? ? hasCompareTarget = true
? ? ? ? } else if (!scheduled) {
? ? ? ? ? const valueToCompare = hasCompareTarget ? compareTarget : this._value
? ? ? ? ? scheduled = true
? ? ? ? ? hasCompareTarget = false
? ? ? ? ? //加入執(zhí)行隊列
? ? ? ? ? scheduler(() => {
? ? ? ? ? ? if (this.effect.active && this._get() !== valueToCompare) {
? ? ? ? ? ? ? triggerRefValue(this)
? ? ? ? ? ? }
? ? ? ? ? ? scheduled = false
? ? ? ? ? })
? ? ? ? }
? ? ? ? // chained upstream computeds are notified synchronously to ensure
? ? ? ? // value invalidation in case of sync access; normal effects are
? ? ? ? // deferred to be triggered in scheduler.
? ? ? ? for (const e of this.dep) {
? ? ? ? ? if (e.computed) {
? ? ? ? ? ? e.scheduler!(true /* computedTrigger */)
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? ? //保證異步方法獲取值時是重新計算的。
? ? ? this._dirty = true
? ? })
? ? this.effect.computed = true
? }
? private _get() {
? ? if (this._dirty) {
? ? ? this._dirty = false
? ? ? return (this._value = this.effect.run()!)
? ? }
? ? return this._value
? }
? get value() {
? ? trackRefValue(this)
? ? // the computed ref may get wrapped by other proxies e.g. readonly() #3376
? ? return toRaw(this)._get()
? }
}
export function deferredComputed<T>(getter: () => T): ComputedRef<T> {
? return new DeferredComputedRefImpl(getter) as any
}

異步計算屬性和計算屬性結構幾乎一致,最為主要的區(qū)別在于ReactiveEffect類型對象的第二個參數上的不同。

這個方法當依賴的某個數據變更時調用,我們先不管第一個if判斷,直接看else里面的內容,簡單來說就是將一個方法放入異步執(zhí)行隊列里面,然后異步執(zhí)行。因為當依賴數據變更時,_dirty屬性被置為了true,所以這個二異步執(zhí)行的方法會去計算最新的值并觸發(fā)依賴更新。

我們現在看if里面的內容,這個分支是通過下面代碼進入的。

for (const e of this.dep) {
? if (e.computed) {
? ? e.scheduler!(true /* computedTrigger */)
? }
}

這兒的設計原理其實是因為當同步的獲取異步計算屬性時,會取到最新的值,當執(zhí)行異步方法時,由于已經獲取過一次數據,為了保證this._get() !== valueToCompare判斷值是true,valueToCompare必須等于重新計算之前的值。

可以通過以下示例解釋:

? ? const src = ref(0)
? ? const c1 = deferredComputed(() => {
? ? ? return src.value % 2
? ? })
? ? const c2 = deferredComputed(() => {
? ? ? return c1.value + 1
? ? })
? ? effect(() => {
? ? ? c2.value
? ? })
? ? src.value = 1
? ? //同步打印c2.value,輸出2
? ? console.log(c2.value);

上述流程,當賦值src.value = 1時,c1執(zhí)行回調,由于c2依賴c1的值,所以c2也會執(zhí)行回調,這兒的回調都是指scheduler方法,_dirty屬性會被置為true,所以在同步打印c2.value的值時,會去重新計算c2,此時c1的_dirty屬性也被置為了true,所以c1的值也會重新計算,即同步打印的c2會取到最新的值。

但需要注意的時,此時異步隊列里面的方法還未執(zhí)行。當同步代碼執(zhí)行完后,開始執(zhí)行異步隊列里面的方法,但執(zhí)行到如下代碼時:

if (this.effect.active && this._get() !== valueToCompare) {
? ? triggerRefValue(this)
}

由于同步打印過c2.value的值,此時_get()方法會從緩存里面取值,如果valueToCompare不等于計算前的值,而直接等于this._value,則判斷為false,不會觸發(fā)下面的依賴更新方法。

異步計算屬性的核心思想,其實就只是把依賴更新的邏輯放入了異步隊列,通過異步的形式執(zhí)行,其主要邏輯和計算屬性幾乎一致,只在細節(jié)上略有不同。 

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Vue.js組件props數據驗證實現詳解

    Vue.js組件props數據驗證實現詳解

    這篇文章主要為大家詳細介紹了Vue.js組件props數據驗證的實現方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • Vue實現手機計算器

    Vue實現手機計算器

    這篇文章主要為大家詳細介紹了Vue實現手機計算器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 在antd Table中插入可編輯的單元格實例

    在antd Table中插入可編輯的單元格實例

    這篇文章主要介紹了在antd Table中插入可編輯的單元格實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Vant中List組件immediate-check=false無效的解決

    Vant中List組件immediate-check=false無效的解決

    這篇文章主要介紹了Vant中List組件immediate-check=false無效的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • 詳解vue-cli中使用rem,vue自適應

    詳解vue-cli中使用rem,vue自適應

    這篇文章主要介紹了vue-cli使用rem,vue自適應,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • 解決vue2.0動態(tài)綁定圖片src屬性值初始化時報錯的問題

    解決vue2.0動態(tài)綁定圖片src屬性值初始化時報錯的問題

    下面小編就為大家分享一篇解決vue2.0動態(tài)綁定圖片src屬性值初始化時報錯的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • vue對于低版本瀏覽器兼容問題的解決思路

    vue對于低版本瀏覽器兼容問題的解決思路

    很多時候使用vue開發(fā)的項目,由于無法在低版本瀏覽器上運行,所以需要解決下,下面這篇文章主要給大家介紹了關于vue對于低版本瀏覽器兼容問題的解決思路,需要的朋友可以參考下
    2023-02-02
  • Vue中created和mounted使用詳解

    Vue中created和mounted使用詳解

    Vue中生命周期包括多個階段,如created和mounted,每階段有特定鉤子函數,生命周期與瀏覽器渲染過程密切相關,了解這些可以優(yōu)化頁面渲染和數據處理,created階段適用于數據初始化,而mounted階段適合進行DOM操作和頁面渲染后的處理
    2024-10-10
  • vue3自己封裝面包屑功能組件的幾種方式

    vue3自己封裝面包屑功能組件的幾種方式

    網站中我們經??吹接袀€導航路徑,可以直觀地顯示當前頁面的路徑,并快速返回之前的任意頁面,這是一個非常實用的功能,也是在Web前端必備的導航UI之一,這篇文章主要給大家介紹了關于vue3自己封裝面包屑功能組件的幾種方式,需要的朋友可以參考下
    2021-09-09
  • Vue項目中首頁長時間白屏的原因以及解決過程

    Vue項目中首頁長時間白屏的原因以及解決過程

    這篇文章主要介紹了Vue項目中首頁長時間白屏的原因以及解決過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論

龙里县| 威远县| 明光市| 彰化市| 历史| 阳朔县| 苗栗县| 怀来县| 平遥县| 枞阳县| 永康市| 赤峰市| 通道| 甘泉县| 南雄市| 公安县| 湖口县| 嘉定区| 和平区| 赫章县| 高青县| 铜山县| 五峰| 陆河县| 台山市| 龙岩市| 枣庄市| 科技| 理塘县| 衢州市| 镇康县| 东平县| 油尖旺区| 台中县| 临武县| 三河市| 灵山县| 庄河市| 汕头市| 民乐县| 通山县|