【Vue3.0】- 計(jì)算屬性

計(jì)算屬性

計(jì)算屬性 API: computed

例子

import { computed, ref } from 'vue' 
const count = ref(1) 
const result = computed(() => count.value + 1) 
console.log(result.value) // 2 
result.value++ // error 
count.value++ 
console.log(result.value) // 3
  • 先使用 ref API 創(chuàng)建了一個(gè)響應(yīng)式對象 count
  • 再使用 computed API 創(chuàng)建了另一個(gè)響應(yīng)式對象 result
  • 修改 count.value 的時(shí)候敢会, result.value 就會(huì)自動(dòng)發(fā)生變化
  • 直接修改 result.value 會(huì)報(bào)一個(gè)錯(cuò)誤
    • 因?yàn)槿绻覀儌鬟f給 computed 的是一個(gè)函數(shù)毒涧,那么這就是一個(gè) getter 函數(shù)钾菊,我們只能獲取它的值扶欣,而不能直接修改它
    • 也可以給 computed 傳入一個(gè)對象吠各,達(dá)到修改的目的
const result = computed({ 
  get: () => count.value + 1, 
  set: val => { 
    count.value = val - 1 
  } 
}) 

computed API 的實(shí)現(xiàn)

function computed(getterOrOptions) { 
  // getter 函數(shù) 
  let getter 
  // setter 函數(shù) 
  let setter 
  // 標(biāo)準(zhǔn)化參數(shù) 
  if (isFunction(getterOrOptions)) { 
    // 表面?zhèn)魅氲氖?getter 函數(shù)蕴轨,不能修改計(jì)算屬性的值 
    getter = getterOrOptions 
    setter = (process.env.NODE_ENV !== 'production') 
      ? () => { 
        console.warn('Write operation failed: computed value is readonly') 
      } 
      : NOOP 
  } 
  else { 
    getter = getterOrOptions.get 
    setter = getterOrOptions.set 
  } 
  // 數(shù)據(jù)是否臟的 
  let dirty = true 
  // 計(jì)算結(jié)果 
  let value 
  let computed 
  // 創(chuàng)建副作用函數(shù) 
  const runner = effect(getter, { 
    // 延時(shí)執(zhí)行 
    lazy: true, 
    // 標(biāo)記這是一個(gè) computed effect 用于在 trigger 階段的優(yōu)先級排序 
    computed: true, 
    // 調(diào)度執(zhí)行的實(shí)現(xiàn) 
    scheduler: () => { 
      if (!dirty) { 
        dirty = true 
        // 派發(fā)通知唠雕,通知運(yùn)行訪問該計(jì)算屬性的 activeEffect 
        trigger(computed, "set" /* SET */, 'value') 
      } 
    } 
  }) 
  // 創(chuàng)建 computed 對象 
  computed = { 
    __v_isRef: true, 
    // 暴露 effect 對象以便計(jì)算屬性可以停止計(jì)算 
    effect: runner, 
    get value() { 
      // 計(jì)算屬性的 getter 
      if (dirty) { 
        // 只有數(shù)據(jù)為臟的時(shí)候才會(huì)重新計(jì)算 
        value = runner() 
        dirty = false 
      } 
      // 依賴收集贸营,收集運(yùn)行訪問該計(jì)算屬性的 activeEffect 
      track(computed, "get" /* GET */, 'value') 
      return value 
    }, 
    set value(newValue) { 
      // 計(jì)算屬性的 setter 
      setter(newValue) 
    } 
  } 
  return computed 
}
  • 主要做了三件事情
  • 1、標(biāo)準(zhǔn)化參數(shù)
    • computed 函數(shù)接受兩種類型的參數(shù)
      • 一個(gè)是 getter 函數(shù)
      • 一個(gè)是擁有 gettersetter 函數(shù)的對象
      • 通過判斷參數(shù)的類型岩睁,我們初始化了函數(shù)內(nèi)部定義的 gettersetter 函數(shù)
  • 2钞脂、創(chuàng)建副作用函數(shù) runner
    • computed 內(nèi)部通過 effect 創(chuàng)建了一個(gè)副作用函數(shù),
    • 它是對 getter 函數(shù)做的一層封裝
    • 創(chuàng)建時(shí)第二個(gè)參數(shù)為effect函數(shù)的配置對象
      • lazytrue: 表示 effect 函數(shù)返回的 runner 并不會(huì)立即執(zhí)行
      • computedtrue :表示這是一個(gè) computed effect捕儒,用于 trigger 階段的優(yōu)先級排序
      • scheduler:表示它的調(diào)度運(yùn)行的方式
  • 3冰啃、創(chuàng)建 computed 對象并返回
    • 擁有 gettersetter 函數(shù)
    • 當(dāng) computed 對象被訪問的時(shí)候
      • 首先會(huì)觸發(fā) getter
      • 然后會(huì)判斷是否 dirty
      • 如果是就執(zhí)行 runner,然后做依賴收集
    • 當(dāng)直接設(shè)置 computed 對象時(shí)
      • 會(huì)觸發(fā) setter肋层,即執(zhí)行 computed 函數(shù)內(nèi)部定義的 setter 函數(shù)

計(jì)算屬性的運(yùn)行機(jī)制

  • 注意
    • computed 內(nèi)部兩個(gè)重要的變量
    • 第一個(gè) dirty 表示一個(gè)計(jì)算屬性的值是否是“臟的”亿笤,用來判斷需不需要重新計(jì)算
    • 第二個(gè) value 表示計(jì)算屬性每次計(jì)算后的結(jié)果
  • 1、當(dāng)渲染階段訪問到計(jì)算屬性栋猖,則觸發(fā)了計(jì)算屬性的getter函數(shù)
get value() { 
  // 計(jì)算屬性的 getter 
  if (dirty) { 
    // 只有數(shù)據(jù)為臟的時(shí)候才會(huì)重新計(jì)算 
    value = runner() 
    dirty = false 
  } 
  // 依賴收集净薛,收集運(yùn)行訪問該計(jì)算屬性的 activeEffect 
  track(computed, "get" /* GET */, 'value') 
  return value 
}
  • 由于默認(rèn) dirtytrue,所以這個(gè)時(shí)候會(huì)執(zhí)行 runner 函數(shù)蒲拉,并進(jìn)一步執(zhí)行 computed getter
  • 如果訪問到了響應(yīng)式對象肃拜,所以就會(huì)觸發(fā)響應(yīng)式對象的依賴收集過程
  • 由于是在 runner 執(zhí)行的時(shí)候訪問到了響應(yīng)式對象,所以這個(gè)時(shí)候的 activeEffectrunner 函數(shù)
  • runner 函數(shù)執(zhí)行完畢雌团,會(huì)把 dirty 設(shè)置為 false
  • 然后執(zhí)行 track(computed,"get",'value') 函數(shù)做依賴收集
  • 這個(gè)時(shí)候 runner 已經(jīng)執(zhí)行完了燃领,所以 activeEffect 是組件副作用渲染函數(shù)
  • 注意:這是兩個(gè)依賴收集過程
    • 對于計(jì)算屬性來說,它收集的依賴是組件副作用渲染函數(shù)
    • 對于計(jì)算屬性中訪問的響應(yīng)式對象來說锦援,它收集的依賴是計(jì)算屬性內(nèi)部的runner函數(shù)
  • 當(dāng)修改計(jì)算屬性時(shí)猛蔽,會(huì)派發(fā)通知,此時(shí)這里不是直接調(diào)用 runner 函數(shù)灵寺,而是把 runner 作為參數(shù)去執(zhí)行 scheduler 函數(shù)
  • trigger函數(shù)內(nèi)部對于 effect 函數(shù)的執(zhí)行方式如下:
const run = (effect) => { 
  // 調(diào)度執(zhí)行 
  if (effect.options.scheduler) { 
    effect.options.scheduler(effect) 
  } 
  else { 
    // 直接運(yùn)行 
    effect() 
  } 
}
  • computed API 內(nèi)部創(chuàng)建副作用函數(shù)時(shí)曼库,已經(jīng)配置了 scheduler 函數(shù)
scheduler: () => { 
  if (!dirty) { 
    dirty = true 
    // 派發(fā)通知,通知運(yùn)行訪問該計(jì)算屬性的 activeEffect 
    trigger(computed, "set" /* SET */, 'value') 
  } 
}
  • 并沒有對計(jì)算屬性求新值略板,而僅僅是把 dirty 設(shè)置為 true
  • 再執(zhí)行 trigger(computed, "set" , 'value')毁枯,去通知執(zhí)行計(jì)算屬性依賴的組件渲染副作用函數(shù),即觸發(fā)組件的重新渲染
  • 在組件重新渲染的時(shí)候叮称,會(huì)再次訪問 計(jì)算屬性种玛,我們發(fā)現(xiàn)這個(gè)時(shí)候 dirtytrue藐鹤,然后會(huì)再次執(zhí)行 computed getter
    image.png

Computed 計(jì)算屬性兩個(gè)特點(diǎn)

  • 1、延時(shí)計(jì)算
    • 只有當(dāng)我們訪問計(jì)算屬性的時(shí)候赂韵,它才會(huì)真正運(yùn)行 computed getter 函數(shù)計(jì)算
  • 2娱节、緩存
    • 它的內(nèi)部會(huì)緩存上次的計(jì)算結(jié)果 value,而且只有 dirtytrue 時(shí)才會(huì)重新計(jì)算
    • 如果訪問計(jì)算屬性時(shí) dirtyfalse右锨,那么直接返回這個(gè) value

Computed 的優(yōu)勢

只要依賴不變化括堤,就可以使用緩存的 value 而不用每次在渲染組件的時(shí)候都執(zhí)行函數(shù)去計(jì)算

嵌套計(jì)算屬性

計(jì)算屬性中訪問另外一個(gè)計(jì)算屬性

const count = ref(0) 
const result1 = computed(() => { 
  return count.value + 1 
}) 
const result2 = computed(() => { 
  return result1.value + 1 
}) 
console.log(result2.value)

計(jì)算屬性的執(zhí)行順序

  • 計(jì)算屬性創(chuàng)建effect時(shí)碌秸,標(biāo)記了computed標(biāo)識的绍移,用于trigger 階段的優(yōu)先級排序
  • trigger 函數(shù)執(zhí)行 effects 的過程
const add = (effectsToAdd) => { 
  if (effectsToAdd) { 
    effectsToAdd.forEach(effect => { 
      if (effect !== activeEffect || !shouldTrack) { 
        if (effect.options.computed) { 
          computedRunners.add(effect) 
        } 
        else { 
          effects.add(effect) 
        } 
      } 
    }) 
  } 
} 
const run = (effect) => { 
  if (effect.options.scheduler) { 
    effect.options.scheduler(effect) 
  } 
  else { 
    effect() 
  } 
} 
computedRunners.forEach(run) 
effects.forEach(run)
  • 在添加待運(yùn)行的 effects 的時(shí)候,會(huì)判斷每一個(gè) effect 是不是一個(gè) computed effect
  • 如果是的話會(huì)添加到 computedRunners
  • 在后面運(yùn)行的時(shí)候會(huì)優(yōu)先執(zhí)行 computedRunners
  • 然后再執(zhí)行普通的 effects

為什么computed runner執(zhí)行優(yōu)先于普通的effect函數(shù)讥电?

  • 因?yàn)楫?dāng)修改響應(yīng)式數(shù)據(jù)時(shí)蹂窖,會(huì)觸發(fā)關(guān)聯(lián)的計(jì)算屬性的runnereffect執(zhí)行
  • 如果先調(diào)用普通effect,這時(shí)dirtyfalse恩敌,使用的數(shù)據(jù)仍然是上次的緩存數(shù)據(jù)瞬测,導(dǎo)致更新不及時(shí)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纠炮,隨后出現(xiàn)的幾起案子月趟,更是在濱河造成了極大的恐慌,老刑警劉巖恢口,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孝宗,死亡現(xiàn)場離奇詭異,居然都是意外死亡耕肩,警方通過查閱死者的電腦和手機(jī)因妇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猿诸,“玉大人婚被,你說我怎么就攤上這事∈崴洌” “怎么了址芯?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窜觉。 經(jīng)常有香客問我谷炸,道長,這世上最難降的妖魔是什么竖螃? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任淑廊,我火速辦了婚禮,結(jié)果婚禮上特咆,老公的妹妹穿的比我還像新娘季惩。我一直安慰自己录粱,他們只是感情好画拾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布啥繁。 她就那樣靜靜地躺著,像睡著了一般青抛。 火紅的嫁衣襯著肌膚如雪旗闽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天蜜另,我揣著相機(jī)與錄音适室,去河邊找鬼。 笑死举瑰,一個(gè)胖子當(dāng)著我的面吹牛捣辆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播此迅,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼汽畴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耸序?” 一聲冷哼從身側(cè)響起忍些,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坎怪,沒想到半個(gè)月后罢坝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芋忿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年炸客,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戈钢。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痹仙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殉了,到底是詐尸還是另有隱情开仰,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布薪铜,位于F島的核電站众弓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏隔箍。R本人自食惡果不足惜谓娃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜒滩。 院中可真熱鬧滨达,春花似錦奶稠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至画株,卻和暖如春辆飘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谓传。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工蜈项, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人良拼。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓战得,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庸推。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容