聊聊 computed 影響性能的場景

大家好,我是若川。持續組織了8個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列

前言

在 Vue 中,computed 是一個非常好用的 API,用于處理派生狀態,又叫“計算屬性”。網上將其用于性能優化的場景比比皆是。

但它也有嚴重影響性能的一面,本文主要是聊聊這種場景。

聊之前,我們先看看它為什么能夠做到性能優化。

computed 的兩個特點

  • 緩存結果:只有依賴項變化的時候才會重新計算,否則復用上一次計算的結果。

  • 惰性求值:只有在真正讀取它的 value 時,才會進行計算求值。

緩存結果

const?todos?=?reactive([{?title:?'點贊',?done:?true},{?title:?'關注',?done:?false?}
])const?openTodos?=?computed(()?=>?todos.filter(todo?=>?!todo.done)
)

在上圖的例子中,只要 todos 不變,多次使用 openTodos 將返回相同的值,不會重新計算。

當 todos 變化時,openTodos 會被標記為 dirty,下次取值時才會進行重新計算。

這點對計算量開銷較大的場景非常有用,確保了只有在必要時才會重新計算。

惰性求值

只有在使用 computed 時,它才會進行計算。如果一個計算屬性,計算開銷非常非常大,但它沒有被任何地方使用,也不會進行求值。

computed 提升性能的場景

如上所說,computed 的延遲計算通常是一件好事:它確保了必要時才會進行計算。

下面是一個簡單的 Todo 例子,簡單說明一下:

  • 有一個 todos 列表

  • showList 用于控制是否展示列表

  • openTodos 是計算未完成 todos 列表的計算屬性,依賴 todos

  • hasOpenTodos 是計算是否有未完成 todos 的計算屬性,依賴 openTodos

<template><button?@click="showList?=?!showList">點擊展示/隱藏</button><template?v-if="showList"><template?v-if="hasOpenTodos"><h2>{{?openTodos.length?}}?Todos:</h2>?<ul><li?v-for="todo?in?openTodos">{{?todo.title?}}</li></ul></template><span?v-else>沒有任何?Todo</span><input?type="text"?v-model="newTodo"><button?type="button"?v-on:click="addTodo">添加?Todo</button></template>
</template><script?setup>
const?showList?=?ref(false)const?todos?=?reactive([{?title:?'點贊',?done:?true},{?title:?'關注',?done:?false?}
])
const?openTodos?=?computed(()?=>?todos.filter(todo?=>?!todo.done)
)
const?hasOpenTodos?=?computed(()?=>?!!openTodos.value.length
)const?newTodo?=?ref('')
function?addTodo()?{todos.push({title:?newTodo.valuedone:?false,})
}
</script>

因為 showList 最初是 false,所以模板中不會讀取 openTodos,不會產生計算。并且無論是一開始還是添加了新的 todo。只有在 showList 設置為 true 之后,模板中才會讀取 openTodos,這才會觸發相應的計算。

這對于開銷大的計算屬性來說,是有很大好處的。

computed 影響性能的場景

惰性求值也會帶來一個缺點:計算屬性的返回結果,只有在對它進行計算后才會知道

聽起來可能比較難以理解,同樣用一個例子來說明:

  • 有一個 list 列表

  • 一個增加 count 的按鈕

  • 一旦 count 超過 100(isOver100),就反向展示 list

當然這里反向展示 list計算量并不大。我們將它想象成一次開銷很大的計算

<template><button?@click="increase">添加計數</button><h3>列表</h3><ul><li?v-for="item?in?sortedList">{{?item?}}</li></ul><>
</template><script?setup>
import?{?ref,?reactive,?computed,?onUpdated?}?from?'vue'const?list?=?reactive([1,2,3,4,5])const?count?=?ref(0)
function?increase()?{count.value++
}const?isOver100?=?computed(()?=>?count.value?>?100)const?sortedList?=?computed(()?=>?{//?這里比較簡單,可以將它想象成開銷大的計算return?isOver100.value???[...list].reverse()?:?[...list]
})onUpdated(()?=>?{//?組件重新渲染時觸發console.log('count?is',?count.value)console.log('component?re-rendered!')
})
</script>

預期情況:

  • 當我們點擊 1-100 次時,因為 count 小于 100,list 不會反向展示,組件不會重新渲染

  • 當點擊第 101 次時, list 才會反向展示,組件這時候才重新渲染

因此,預期總計重新渲染 1 次

但運行結果告訴我們,組件會重新渲染 101 次!!

5a31454e352dd23ed759bf14fc94cd99.png

讓我們一步一步來看發生了什么。

依賴關系如圖:

cfa9d5ea6ba152db1585b16d5ba6e8e1.png

  1. 點擊按鈕,計數增加。由于模板中沒有使用 count,理論上不會重新渲染。

  2. 但 count 改變后,依賴 count 的計算屬性 isOver100 被標記為 dirty,在下次使用時需要重新計算,并告知了它的訂閱者。

  3. 因為 sortedList 依賴 isOver100,收到 isOver100 的通知后,它也被標記為 dirty,在下次使用時需要重新計算,并告知了它的訂閱者。

  4. 最終模板中使用了 sortedList,所以收到 sortedList 的更新通知后,組件重新渲染了。

  5. 重新渲染時,計算 sortedList,接著計算 isOver100,但現在由于 count 不到 100,isOver100 仍然返回 false

  6. 最終 sortedList 計算結果與原來一致,重新渲染后 DOM 無任何改變,但是我們卻運行了多次大開銷的 sortedList 計算!

簡單來說:因為 count 變了,所以 isOver100 “覺得”自己變了,需要重新算(但其實沒有),就讓依賴它的 sortedList 重新計算了。

根本原因就是 isOver100,它是一個頻繁計算且計算非常簡單的 computed,多次計算返回值也與之前相同(都為 false)。它只發揮了 computed 狀態派生的作用。

但在計算開銷大的 sortedList 中,依賴了廉價的 isOver100因為 computed 是惰性求值的,isOver100 的計算結果只能在渲染時重新計算才會知道,所以 sortedList 也只能在渲染時等待它的計算結果再重新計算,哪怕最終結果一致。導致觸發了不必要的重新渲染,用的不好會嚴重影響性能。

所以影響性能的 computed,通常都有這樣的特征:

  1. 一個計算簡單的 computed,頻繁觸發計算,并且返回值通常變化不大,比如上面的 boolean 類型

  2. 另一個計算開銷大的 computed,依賴這個廉價的 computed

如何解決

我們發現根因是由于 computed 的惰性求值,讓 isOver100 "覺得"自己變了,需要重新計算,導致了這一系列的連鎖反應。但因為它的計算是廉價的,頻繁計算也不會影響性能。

677d5eb167f416bc919ada30209fa427.png

有沒有辦法不要 computed 的延遲計算呢?在 isOver100 "覺得"自己變了的時候馬上就能知道是不是真的變了。在發現自己其實沒變后,不再通知訂閱者,也就沒有了后續的重新渲染。

我們可以將它的計算提前,在依賴變化時就立刻計算得到結果。

設計一種立刻求值eagerComputed

Vue 的響應式系統為我們提供了構建自定義 computed 所必要的工具。手動點贊👍
如果項目已經引入了 vueuse,可直接使用

import?{?watchEffect,?shallowRef,?readonly?}?from?'vue'export?function?eagerComputed(fn)?{const?result?=?shallowRef()watchEffect(()?=>?{result.value?=?fn()},?{flush:?'sync'?//?使用同步觀察器,依賴變化時立刻求值})return?readonly(result)
}

eagerComputed 與 computed 用法一致,只是行為上不同,在依賴變化時,它會立刻進行求值。

什么時候使用 computed 和 eagerComputed

  1. 復雜的計算使用 computed,可以受益于緩存結果和惰性求值。

  2. 簡單的計算使用 eagerComputed,因為每次依賴項變化時它都會重新計算。

回到上面的例子中,我們將 isOver100 改為 eagerComputed

const?isOver100?=?eagerComputed(()?=>?count.value?>?100);

現在,當點擊按鈕 101 次時,組件才會重新渲染。

9bd176520442c98ff605c847a0e588a1.png

我們打個接地氣的比方:

比方代碼
大家吃飯后都會“覺得”自己體重漲了isOver100 "覺得"自己變了
為了驗證漲沒漲,需要下樓去稱體重,因為家里沒有要讓 sortedList 重新計算
下樓是個麻煩的事情sortedList 的計算開銷很大
稱完發現并沒漲,白跑一趟isOver100 其實沒變
為了不每次都白跑一趟,我們可以在家里買個秤使用 eagerComputed,變沒變馬上就知道

結語

我們深入了解了 computed 的工作原理與兩大特性:緩存結果和惰性求值。掌握了什么場景會優化性能,什么場景會影響性能,對于影響性能的場景,可以使用 eagerComputed 避免不必要的響應式更新來解決性能問題。

參考

  • https://dev.to/linusborg/vue-when-a-computed-property-can-be-the-wrong-tool-195j


c831f295bbf7bb3e85b4a21c14bcd369.gif

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經堅持寫了8年,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。

143ee9130137b7d145c8fba086c95999.png

掃碼加我微信 ruochuan02、拉你進源碼共讀

今日話題

略。分享、收藏、點贊、在看我的文章就是對我最大的支持~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274789.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274789.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274789.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

saej1929_(1929年-2020年)

saej1929Milton Glaser, the legendary graphic designer who co-founded New York Magazine, created the iconic ‘I ? NY’ logo, the psychedelic Bob Dylan poster, and the Brooklyn Brewery logo, passed away yesterday at the age of 91 on his birthday, June 26, 2…

Chap2-構造函數語意學

如果一個類沒有任何constructor&#xff0c;那么會有一個default constructor被隱式的聲明出來&#xff0c;一個implicit default constructor將是一個trivial&#xff08;無用的&#xff09;constructor。但是在某些情況下&#xff0c;implicit default constructor將是一個no…

【熱點】React18正式版發布,未來發展趨勢是?

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列2022年…

不要重新發明輪子_是否重新發明輪子

不要重新發明輪子Design is a profession that thrives on creativity. Us designers are constantly trying to innovate by thinking outside the box. We’ve seen design evolve across all sectors — print, digital, product, architecture etc. We have gone from type…

asp.net mvc批量刪除的實現

<form action"Index" method"post"> <div> {<table><thead> <tr> <th width"100">編號</th><th width"100">名字</th></tr></thead> <tbody> foreach (var…

點擊頁面元素,這個Vite插件竟然幫我打開了Vue組件文件!超級好用!

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列前言這…

shields 徽標_符號,標志,文字標記:徽標類型的綜合指南

shields 徽標Designers and non-designers alike struggle with common terminology when talking about brand marks, often using different terms interchangeably. When it comes to clarifying definitions, sometimes even the most seasoned professionals get confused…

【原創】SVM小結

理論基礎&#xff1a; 機器學習有三類基本的問題&#xff0c;即模式識別、函數逼近和概率密度估計&#xff0e; SVM有著嚴格的理論基礎&#xff0c;建立了一套較好的有限訓練樣本下機器學習的理論框架和通用方法。他與機器學習是密切相關的&#xff0c;很多理論甚至解決了機器學…

React 18 帶給我們的驚喜

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列這篇文…

建模心法(2)——邁出建模第一步

原文地址&#xff1a;http://www.cnblogs.com/1-2-3/archive/2008/08/04/model-method-part1.html 原文作者&#xff1a;景春雷 一錯再錯的這故事才精彩 ——樸樹 《我愛你再見》摘要 即使讀了再多的書、跟過再多的項目&#xff0c;…

Web:你知道我這十幾年是怎么過來的嗎?!

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列1989 …

設計師更高效_如何丟掉我的工作使我成為一名更好的設計師

設計師更高效I lost my job a few times early on in my design career. In the process of getting back up after a job loss, it has made me a better designer not only in terms of hard skills but the soft skills required to be more resilient and empathetic, whic…

【ASP.NET】登陸成功后如何跳轉到上一個頁面

當用戶瀏覽網頁的時候會在某個地方需要用戶登陸才能繼續瀏覽&#xff0c;用戶登陸之后會自動跳轉到剛剛瀏覽的頁面。這個步驟是怎么實現的呢&#xff1f;net小伙在查閱相關資料實踐之后終于明白了&#xff0c;其實很簡單&#xff0c;先分享給大家吧。 當用戶在瀏覽一個頁面的時…

4月,誠邀你參加源碼共讀,學會看源碼,打開新世界!開闊視野

大家好&#xff0c;我是若川。很多關注我的新朋友可能不知道我組織了源碼共讀活動~也有很多人不知道我是誰。有人以為我是80后。有人以為我是全職自媒體等等。若川的 2021 年度總結&#xff0c;彈指之間 這篇文章寫了我是16年畢業的&#xff0c;或許有些啟發。源碼共讀按照從易…

bt709和srgb_選擇用于多用途視頻編輯和色彩校正的顯示器— sRGB,DCI-P3,REC 709

bt709和srgb**Note from the author: if you enjoy this article, please follow me or this publication for more video production and marketing related content.****作者注&#xff1a;如果您喜歡本文&#xff0c;請關注我或此出版物以獲取更多與視頻制作和營銷相關的內容…

超4000人參加源碼共讀,喊你來一起學習成長~打開新世界

大家好&#xff0c;我是若川。很多關注我的新朋友可能不知道我組織了源碼共讀活動~也有很多人不知道我是誰。有人以為我是80后。有人以為我是全職自媒體等等。若川的 2021 年度總結&#xff0c;彈指之間 這篇文章寫了我是16年畢業的&#xff0c;或許有些啟發。源碼共讀按照從易…

figma設計_如何在Figma中構建設計入門套件(第二部分)

figma設計Figma教程 (Figma Tutorial) With this short, but informative Tutorial Series I aim to show you how to build the solid foundations of a powerful, and versatile Design Starter Kit, enabling you to start your next project in Figma faster than ever bef…

Hibernate 簡介(百度)

Hibernate是一個開放源代碼的對象關系映射框架&#xff0c;它對JDBC進行了非常輕量級的對象封裝&#xff0c;使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。 Hibernate可以應用在任何使用JDBC的場合&#xff0c;既可以在Java的客戶端程序使用&#xff0c;也可以在…

GitHub 最受歡迎的Top 20 JavaScript 項目

大家好&#xff0c;我是若川。持續組織了8個月源碼共讀活動&#xff0c;感興趣的可以點此加我微信 ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試系列今天來…

java反編譯,eclipse支持插件

http://java.decompiler.free.fr/?qjdeclipse 按照說明 在eclipse更新插件就可以。 這樣 在一些 閉源的jar文件&#xff0c;你也可以看到 大致的源碼。&#xff08;公司 知道如何 加密混淆 java代碼或class文件&#xff0c;居然無法使用jd-gui瀏覽源碼&#xff09; 而&#xf…