計(jì)算屬性 computed

?????? 前言: 今天想和大家分享一下計(jì)算屬性 computed畅姊,使用過(guò)的童鞋 大致都了解 computed 會(huì)緩存鸵钝,并且在其依賴的響應(yīng)式數(shù)據(jù)發(fā)生變化的時(shí)候進(jìn)行重新計(jì)算更新數(shù)據(jù)疏哗,那為什么 computed 會(huì)進(jìn)行緩存呢呛讲,內(nèi)部又是怎么實(shí)現(xiàn)的呢禾怠,下面我們來(lái)一起一探究竟??????

一、構(gòu)建基礎(chǔ)列表

大致目錄結(jié)構(gòu)
---| packages
---|---| reactivity // 響應(yīng)性模塊
---|---|---| src
---|---|---|---| index.ts 出口文件
---|---|---|---| ref.ts
---|---|---|---| reactive.ts
---|---|---|---| computed.ts
---|---|---|---| effect.ts
---|---|---|---| dep.ts
---|---|---|---| baseHandlers.ts
---|---| shared // 公共方法模塊
---|---|---| src
---|---|---|---| index.ts 出口文件
---|---|---|---| shapeFlags.ts
---|---| vue // 打包贝搁、測(cè)試實(shí)例吗氏、項(xiàng)目整體入口模塊
---|---|---| dist
---|---|---| examples
---|---|---| src
---|---|---|---| index.ts 出口文件

二、開(kāi)整

computed 目標(biāo):構(gòu)建 computed 函數(shù)雷逆,分析為什么 computed 會(huì)進(jìn)行數(shù)據(jù)緩存弦讽,什么場(chǎng)景適用 computed?
1. 創(chuàng)建 packages/reactivity/src/computed.ts 模塊
import { isFunction } from '@vue/shared'
import { Dep } from './dep'
import { ReactiveEffect } from './effect'
import { trackRefValue, triggerRefValue } from './ref'

/**
* 計(jì)算屬性
*/
export function computed(getterOrOptions) {
  let getter
  // 判斷傳入的參數(shù)是否為一個(gè)函數(shù)
  const onlyGetter = isFunction(getterOrOptions)
  
  // 如果:為函數(shù)膀哲, 則: 賦值給 getter
  if(onlyGetter) {
    getter = getterOrOptions
  }
  // new 一個(gè)計(jì)算屬性
  const cRef = new ComputedRefImpl(getter)
  return cRef as any
}

/**
* 計(jì)算屬性類
*/
export class ComputedRefImpl<T> {
 public dep?: Dep = undefined // 引用當(dāng)前 computed 的 effect 的 set
 public readonly effect: ReactiveEffect<T> // effect 對(duì)象
 public readonly __v_isRef = true // ref標(biāo)識(shí)
 
 private _value!: T // 緩存值
 
 constructor(getter) {
   this.effect = new ReactiveEffect(getter)
   // effect 實(shí)例 被掛載了一個(gè)新的屬性 computed 為當(dāng)前的 ComputedRefImpl 的實(shí)例
   this.effect.computed = this
 }
 
 /**
 * get value 在ref 中 似曾相識(shí)的 操作
 */
 get value() {
   // 依賴收集 (this 本質(zhì)上就是 ComputedRefImpl 的實(shí)例)
   trackRefValue(this)
   // 執(zhí)行 run 函數(shù)
   this._value = this.effect.run()!
   // 返回計(jì)算之后的真實(shí)值
   return this._value
   
 }
}
packages/shared/src/index.ts 增加 isFunction 工具類
/**
* 判斷是否是一個(gè) function
*/
export function isFunction = (val: unknown): val is Function => typeof val === 'function'
packages/reactivity/src/effect.ts 增加 computed 屬性
/**
* 響應(yīng)數(shù)據(jù)觸發(fā)依賴時(shí)的執(zhí)行類
*/
export class ReactiveEffect<T = any> {
  // 存在該屬性往产,則表示當(dāng)前 effect 為計(jì)算屬性的 effect
  computed?: ComputedRefImpl<T>
  ...
}

至此,我們已經(jīng)通過(guò) get value() 完成了依賴收集某宪,下面我們來(lái)完善一下依賴觸發(fā)操作


2. packages/reactivity/src/computed.ts 模塊 關(guān)于臟狀態(tài) _dirty 和調(diào)度器 scheduler

在依賴觸發(fā)模塊仿村,我們需要先了解一下臟狀態(tài)和調(diào)度器大致指的是什么,作用是什么兴喂,下面是簡(jiǎn)單的介紹蔼囊。

  • 臟狀態(tài)(_dirty)即是數(shù)據(jù)臟了,是控制緩存的終極幕后衣迷,具體怎么控制畏鼓,下面通過(guò)代碼描述

    • false 使用緩存值,免于計(jì)算
    • true 觸發(fā)run方法更新數(shù)據(jù)壶谒,重新計(jì)算
  • 調(diào)度器(scheduler)簡(jiǎn)單描述是回調(diào)函數(shù) fn云矫,在當(dāng)前computed上下文中,只要 effect 中存在 scheduler佃迄,就會(huì)執(zhí)行該函數(shù)泼差。

export class ComputedRefImpl<T> {
  public _dirty = true // 臟狀態(tài)
  
  constructor(getter) {
    this.effect = new ReactiveEffect(getter, () => {
      // 判斷當(dāng)前臟數(shù)據(jù)的狀態(tài),false:觸發(fā)依賴
      if(!this._dirty) {
       // 將臟數(shù)據(jù)改為 true
       this._dirty = true
       // 觸發(fā)依賴
       triggerRefValue(this)
      }
    })
    this.effect.computed = this
   }
 }

 get value() {
   // 收集依賴
   trackRefValue(this)
   // 判斷當(dāng)前臟數(shù)據(jù)的狀態(tài)呵俏,_dirty: true堆缘,表示需要重新執(zhí)行 run,獲取最新數(shù)據(jù)
   if(this._dirty) {
     this._dirty = false
     // 執(zhí)行 run 函數(shù)
     this._value = this.effect.run()普碎!
   }
   // 返回計(jì)算后的數(shù)據(jù)
   return this._value
 }
}
packages/reactivity/src/effect.ts 新增調(diào)度器相關(guān)內(nèi)容
export type EffectScheduler = (...args:any[]) => any


/**
* 響應(yīng)數(shù)據(jù)觸發(fā)依賴時(shí)的執(zhí)行類
*/
export class ReactiveEffect<T = any> {
  // 存在該屬性吼肥,則表示當(dāng)前 effect 為計(jì)算屬性的 effect
  computed?: ComputedRefImpl<T>
  
  constructor(
   public fn: () => T,
   public scheduler: EffectScheduler | null = null
  ){}
  ...
}


/**
* 觸發(fā)指定依賴
*/
export function triggerEffect(effect: ReactiveEffect) {
 // 存在調(diào)度器則執(zhí)行調(diào)度函數(shù)
 if(effect.scheduler) {
   effect.scheduler()
 }
 // 否則直接執(zhí)行 run 函數(shù)
 else {
  effect.run()
 }
}

至此,我們的computed的五臟六腑已都實(shí)現(xiàn)麻车,下面來(lái)小試牛刀一下缀皱,看看是否可以正常運(yùn)行??

3. 創(chuàng)建 packages/vue/examples/reactivity/computed.html
<body>
  <div id="app"></div>
  <script>
    const { reactive, effect, computed } = Vue
    const obj = reactive({
     name: '張三'
    })
    const computedObj = computed(() => {
      return '姓名:' + obj.name
    })
    effect(() => {
      // 校驗(yàn):多次觸發(fā),computed由于緩存原因應(yīng)該只會(huì)計(jì)算一次
      document.querySelector('#app').innerHTML = computedObj.value
      document.querySelector('#app').innerHTML = computedObj.value
    })
    
    setTimeout(() => {
      obj.name = '李四'
    }, 2000)
  </script>
</body>

運(yùn)行一下以上實(shí)例动猬,computed是否真的會(huì)只進(jìn)行一次計(jì)算呢啤斗?還是會(huì)出其他的狀況呢,各位看官赁咙,下回我們?cè)偌?xì)細(xì)道來(lái)~~~~

學(xué)習(xí)使我快樂(lè).mmp
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钮莲,一起剝皮案震驚了整個(gè)濱河市免钻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崔拥,老刑警劉巖极舔,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異链瓦,居然都是意外死亡拆魏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門慈俯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)渤刃,“玉大人,你說(shuō)我怎么就攤上這事肥卡∠疲” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵步鉴,是天一觀的道長(zhǎng)揪胃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)氛琢,這世上最難降的妖魔是什么喊递? 我笑而不...
    開(kāi)封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮阳似,結(jié)果婚禮上骚勘,老公的妹妹穿的比我還像新娘。我一直安慰自己撮奏,他們只是感情好俏讹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著畜吊,像睡著了一般泽疆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玲献,一...
    開(kāi)封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天殉疼,我揣著相機(jī)與錄音,去河邊找鬼捌年。 笑死瓢娜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的礼预。 我是一名探鬼主播眠砾,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼托酸!你這毒婦竟也來(lái)了褒颈?” 一聲冷哼從身側(cè)響起伙单,我...
    開(kāi)封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哈肖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體念秧,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淤井,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摊趾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片币狠。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖砾层,靈堂內(nèi)的尸體忽然破棺而出漩绵,到底是詐尸還是另有隱情,我是刑警寧澤肛炮,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布止吐,位于F島的核電站,受9級(jí)特大地震影響侨糟,放射性物質(zhì)發(fā)生泄漏碍扔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一秕重、第九天 我趴在偏房一處隱蔽的房頂上張望不同。 院中可真熱鬧,春花似錦溶耘、人聲如沸二拐。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)百新。三九已至,卻和暖如春留荔,著一層夾襖步出監(jiān)牢的瞬間吟孙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工聚蝶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杰妓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓碘勉,卻偏偏與公主長(zhǎng)得像巷挥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子验靡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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