91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3計算屬性怎么實現

發布時間:2022-08-30 13:55:15 來源:億速云 閱讀:172 作者:iii 欄目:開發技術

今天小編給大家分享一下Vue3計算屬性怎么實現的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

計算屬性

Vue3的官方文檔中,對于計算屬性有這樣的描述:

  • 對于任何包含響應式數據的復雜邏輯,我們都應該使用計算屬性

  • 計算屬性只會在相關響應式依賴發生改變時重新求值

從上面的描述可以明確計算屬性的需求,計算屬性計算的是響應式數據(滿足描述1),且計算結果應當被緩存(滿足描述2)。讓我們一個一個來實現,先使用computed創建一個計算屬性。

function effect(fn) { // 副作用函數
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  effectFn.deps = [] 
  effectFn()
}
...
const data = { foo: 1, bar: 2 }
const obj = new Proxy(data, { // 響應式對象
  get(target, key) {
    track(target, key)
    return target[key]
  },
  set(target, key, newValue) {
    target[key] = newValue
    trigger(target, key)
    return true
  }
})
...
const sumRes = computed(() => obj.foo + obj.bar) // (1)
console.log(sumRes.value)

在(1)處,我們簡單寫了一個計算屬性的功能,為了實現通過sumRes.value讀取計算屬性值功能,在實現計算屬性時,需要返回一個對象,通過對象內的get觸發副作用函數。

function computed(getter) {
  const effectFn = effect(getter)
  const obj = {
    get value() {
      return effectFn()
    }
  }
  return obj
}

但這個函數顯然是無法執行的,這是因為前面我們在實現effect時,需要直接執行副作用函數,不需要提供返回值。沒有返回值,computed自然無法獲取到effect的執行結果。因此,當在計算屬性中使用effect時,需要將副作用函數返回給計算屬性,由計算屬性決定何時執行,而不再由effect立即執行(即懶執行)。

為了實現這點,就需要向effect中添加一個開關lazy,考慮到我們可能將來還需要對effect配置其它特性,我們使用一個對象options來封裝這個開關。

function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn() // (1)
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res // (2)
  }
  effectFn.deps = []
  effectFn.options = options // (3)
  if (!options.lazy) { // (4)
    effectFn()
  }
  return effectFn // (5)
}

我們在(4)處放置了lazy開關,不需要懶執行的副作用函數同樣會自動執行。在(1)(2)(5)處返回了副作用函數的結果,供懶執行使用。同時在(3)處向下傳遞了options,保證在effect發生嵌套時,也使得副作用函數執行預期的行為。基于上述effect的修改,我們在computed中設置lazy開關。

function computed(getter) {
  const effectFn = effect(getter, { lazy: true })
  const obj = {
    get value() { // (6)
      return effectFn()
    }
  }
  return obj
}
const sumRes = computed(() => obj.foo + obj.bar)

Vue3計算屬性怎么實現

從上圖中可以看出,我們已經實現了描述1,即使用計算屬性進行響應式數據的計算,當響應式數據的值發生變化時,計算屬性的值也會隨之改變。但觀察上文代碼的(6)處,不難發現,無論什么情況下,只要讀取sumRes.value的值,就會觸發一次副作用函數,使其重新進行可能不必要的執行。所以接著,我們嘗試實現描述2,緩存計算屬性的結果。

先從最簡單的入手,我們用一個變量value來緩存上次計算的值,并添加一個dirty開關,記錄是否需要重新觸發副作用函數。

function computed(getter) {
  let value
  let dirty = true
  const effectFn = effect(getter, { lazy: true })
  const obj = {
    get value() {
      if(dirty) {
        value = effectFn()
        dirty = false
      }
      return value
    }
  }
  return obj
}

修改之后,緩存的值就能生效了。但這樣做產生了一個明顯的BUG,當dirty的值被置為false時,無法再變為true,這也就意味著,無論響應式數據obj.barobj.foo如何變化,計算屬性的值永遠都只能是緩存的值value,如下圖所示。

Vue3計算屬性怎么實現

為了解決這個問題,我們需要一種方式,能夠在obj.barobj.foo的值變化時,在獲取sumRes.value之前,將dirty開關的值置為true。受前面懶加載的啟發,我們嘗試能不能通過配置options來實現這個功能。

const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return target[key]
  },
  set(target, key, newValue) {
    target[key] = newValue
    trigger(target, key)
    return true
  }
})

再來回憶一下響應式對象的整個流程,當響應式對象中的數據被修改時,執行了trigger去觸發收集的副作用函數。而在計算屬性中,我們不再需要自動的觸發副作用函數。所以自然會想到,能否在這個地方將dirty置為true呢?按照這個思路,我們先對trigger進行修改。

function trigger(target, key) {
  const propsMap = objsMap.get(target)
  if(!propsMap) return
  const fns = propsMap.get(key)
  const otherFns = new Set()
  fns && fns.forEach(fn => {
    if(fn !== activeEffect) {
      otherFns.add(fn)
    }
  })
  otherFns.forEach(fn => {
    if(fn.options.scheduler) { // (7)
      fn.options.scheduler()
    } else {
      fn()
    }
  })
}

按照前文的思路,我們在(7)處增加了一個判斷,如果副作用函數fn的配置項options中含有scheduler函數,我們就執行scheduler而非副作用函數fn。我們稱這里的scheduler調度器。相應的,我們在計算屬性中添加調度器。

function computed(getter) {
  let value
  let dirty = true
  const effectFn = effect(getter, { 
    lazy: true,
    scheduler() { // (8)
      dirty = true
    } 
  })
  const obj = {
    get value() {
      if(dirty) {
        value = effectFn()
        dirty = false
      }
      return value
    }
  }
  return obj
}

在(8)處我們添加了調度器。添加調度器后,讓我們再來串一下整個流程,當響應式數據被修改時,才會執行trigger函數。由于我們傳入了調度器,因此trigger函數在執行時不再觸發副作用函數,轉而執行調度器,此時dirty開關的值變為了true。當程序再往下執行時,由于dirty已經變為true,副作用函數就可以正常被手動觸發。效果如下圖所示。

Vue3計算屬性怎么實現

到這里為止,計算屬性在功能上已經實現完畢了,讓我們嘗試完善它。在Vue中,當計算屬性中的響應式數據被修改時,計算屬性值會同步更改,進而重新渲染,并在頁面上更新。渲染函數也會執行effect,為了說明問題,讓我們使用effect簡單的模擬一下。

const sumRes = computed(() => obj.foo + obj.bar)
effect(() => console.log('sumRes =', sumRes.value))

這里我們的預期是當obj.fooobj.bar改變時,effect會自動重新執行。這里存在的問題是,正常的effect嵌套可以被自動觸發(這點我們在上一篇博客中已經實現了),但sumRes包含的effect僅會在被讀取value時才會被get觸發執行,這就導致響應式數據obj.fooobj.bar無法收集到外部的effect,收集不到的副作用函數,自然無法被自動觸發。

function computed(getter) {
  let value
  let dirty = true
  const effectFn = effect(getter, { 
    lazy: true,
    scheduler() {
      dirty = true
      trigger(obj, 'value') // (9)
    } 
  })
  const obj = {
    get value() {
      if(dirty) {
        value = effectFn()
        dirty = false
      }
      track(obj, 'value') // (10)
      return value
    }
  }
  return obj
}

在(10)處我們手動收集了副作用函數,并當響應式數據被修改時,觸發它們。

Vue3計算屬性怎么實現

使用微任務優化調度器

在設計調度器時,我們忽略了一個潛在的問題。

還是先來看一個例子:

effect(() => console.log(obj.foo))
for(let i = 0; i < 1e5; i++) {
  obj.foo++
}

Vue3計算屬性怎么實現

隨著響應式數據obj.foo被不停修改,副作用函數也被不斷重復執行,在明顯的延遲之后,控制臺打印出了大量的數據。但在前端的實際開發中,我們往往只關心最終結果,拿到結果顯示在頁面上。在這種條件下,我們如何避免副作用函數被重復執行呢?

const jobQueue = new Set() // (11)
const p = Promise.resolve() // (12)
let isFlushing = false // (13)
function flushJob() { // (14)
  if (isFlushing) return
  isFlushing = true
  p.then(() => {
    jobQueue.forEach(job => {
      job()
    })
  }).finally(() => {
    isFlushing = false
  })
}

這里我們的思路是使用微任務隊列來進行優化。(11)處我們定義了一個Set作為任務隊列,(12)處我們定義了一個Promise來使用微任務。精彩的部分來了,我們的思路是將副作用函數放入任務隊列中,由于任務隊列是基于Set實現的,因此,重復的副作用函數僅會保留一個,這是第一點。接著,我們執行flushJob(),這里我們巧妙的設置了一個isFlushing開關,這個開關保證了被微任務包裹的任務隊列在一次事件循環中只會執行一次,而執行的這一次會在宏任務完成之后觸發任務隊列中所有不重復的副作用函數,從而直接拿到最終結果,這是第二點。按照這個思路,我們在effect中添加調度器。

effect(() => { console.log(obj.foo) }, {
  scheduler(fn) {
    jobQueue.add(fn)
    flushJob()
  }
})

Vue3計算屬性怎么實現

需要注意的是,瀏覽器在執行微任務時,會把微任務隊列中的所有微任務一口氣做完。因此,微任務在執行時會阻塞頁面的渲染。所以在實際使用時,要注意避免在微任務隊列中放置過重的任務,以免引起卡頓。

以上就是“Vue3計算屬性怎么實現”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

吴堡县| 师宗县| 大埔区| 玉环县| 宕昌县| 德清县| 彭州市| 吕梁市| 广宁县| 张北县| 二连浩特市| 大同县| 平邑县| 邓州市| 吉安县| 惠水县| 铜川市| 罗田县| 汤阴县| 新干县| 昭觉县| 双牌县| 神池县| 富平县| 绍兴县| 福贡县| 奉化市| 麻阳| 海安县| 唐山市| 高青县| 汉沽区| 黄石市| 英德市| 英吉沙县| 宝丰县| 普定县| 治多县| 枣强县| 西城区| 沾化县|