網站首頁 編程語言 正文
引言
前幾天寫的一篇介紹use
這個新hook
的文章中聊到React
原生實現了一個緩存函數的方法 ——?cache
。
對于如下代碼,被cache
包裹的函數,當多次調用時,如果傳參不變,會始終返回緩存值:
const cacheFn = cache(fn); cacheFn(1, 2, 3); // 不會執行fn,直接返回緩存值 cacheFn(1, 2, 3);
React
內為什么需要cache
方法呢?考慮如下組件:
const fetch = cache(fetchUserData); function User({id}) { const {name} = use(fetch(id)); return <p>{name}</p>; }
User
組件會根據用戶id
請求用戶數據,并渲染用戶名。
如果id
改變,那么fetch
方法重新發起請求是正常邏輯。
但是,React
組件經常render
,如果在id
不變的情況下,由于User
組件render
導致不斷發起請求,顯然是不合理的。
所以,這種情況下就需要cache
方法。當id
不變時,即使User
組件反復render
,fetch(id)
都返回同一個值。
本文來聊聊cache
的源碼實現。
分析實現思路
整個方法實現一共有64行代碼,首先我們來分析下實現要點。
如果參數不變,則使用緩存的值。這意味著我們需要處理:
參數的順序
舉個例子,當參數順序變了,不使用緩存值:
const cacheFn = cache(fn); cacheFn(1, 2, 3); // 不使用緩存值 cacheFn(3, 2, 1);
區別處理引用類型、原始類型參數
舉個例子,當同一位置的參數傳遞了同一個引用類型值,則返回緩存值:
const cacheFn = cache(fn); const obj = {}; cacheFn(1, obj, 3); // 返回緩存值 cacheFn(1, obj, 3);
當同一位置的參數傳遞了不同引用類型值,則不返回緩存值:
const cacheFn = cache(fn); const obj = {}; cacheFn(1, obj, 3); // 不返回緩存值 cacheFn(1, {}, 3);
緩存的垃圾回收
緩存數據時,要注意緩存失效但是引用的數據沒有釋放造成的內存泄漏問題。
所以,對于引用類型數據,可以使用WeakMap
保存。
對于原始類型數據,可以使用Map
保存。
WeakMap
與Map
的區別在于 —— 在WeakMap
中,key
到他對應的value
是弱引用。這意味著當沒有其他數據引用這個key
時,他可以被垃圾回收。而在Map
中,key
到value
是強引用,即使沒有其他數據引用這個key
,他也不會被垃圾回收。
實現原理
本文不會介紹具體的代碼實現(大段貼代碼讓人看起來頭疼)。
我會用示例圖講解實現原理。了解原理后,如果你對實現細節感興趣,可以參考:
cache的源碼實現PR
cache的在線示例
對于如下代碼:
const cacheFn = cache(fn); const obj = {}; cacheFn(1, obj, 3);
cacheFn
的每個傳參,對應cache
內部的一個cacheNode
節點:
// CacheNode構造函數 function createCacheNode<T>(): CacheNode<T> { return { s: UNTERMINATED, v: undefined, o: null, p: null }; }
字段的意義如下:
- s:
cacheNode
的緩存狀態,有 未中止/中止/發生錯誤 3種狀態 - v:
cacheNode
緩存的值 - o:緩存的引用類型值
- p:緩存的原始類型值
上述cacheFn
執行后會生成如下cacheNode
鏈式結構:
讓我們看看這個鏈式結構如何解決文章開篇提到的3個問題。
如何解決參數的順序?
可以看到,上圖中最后一個cacheNode
節點的狀態(cacheNode.s
)為中止。
如果后續執行cacheFn
傳入相同的參數,則會復用緩存的cacheNode
節點。
如果所有傳參都相同,那么會復用完整的cacheNode
鏈,此時最后一個cacheNode
節點為中止狀態,則不需要重新執行cacheFn
方法計算返回值,而是直接返回緩存的值(cacheNode.v
)。
如果后續執行cacheFn
,傳入新的參數,則前后的cacheNode
鏈不會一致。
比如:
// 第一次 cacheFn(1, obj, 3); // 第二次 cacheFn(1, 3, obj);
則第二次生成的cacheNode
鏈中,第二個節點就與之前不同(之前obj,之后3),則后續cacheNode
節點也不會相同。
通過這種鏈式結構,保證了只有當所有參數保持一致,才能返回緩存的值。否則將重新執行函數,并緩存新的返回值與cacheNode
鏈。
如何處理引用類型值
可以從圖中發現,對于引用類型參數(比如示例中的obj
),對應一個weakMap
節點。
這不僅意味著當沒有其他數據引用他時,這個cacheNode
節點能夠釋放內存,同時也意味著這個cacheNode
之后的cacheNode
鏈會斷掉,他們占用的內存也會釋放。
而原始類型值不存在這樣的問題,從圖中可以發現,原始類型值對應一個map
節點。
總結
cache
方法是React
內部實現,未來會暴露給開發者使用的緩存方法,可以緩存任意函數。
當多次執行并傳遞相同的參數給cache
包裹的函數時,后續執行會返回緩存的值。
這是為了應對某些函數需要在React組件多次render間返回穩定的值的場景。
比如:對于相同的傳參,請求數據的函數返回同一個promise
。
cache
的實現方式是 —— 基于傳參,構造一條cacheNode
鏈,傳參的穩定對應了鏈表的穩定,并最終對應了返回值的穩定。
原文鏈接:https://segmentfault.com/a/1190000042698506
相關推薦
- 2022-03-21 C++遞歸實現選擇排序算法_C 語言
- 2022-04-09 C語言實現計算器的兩種方法_C 語言
- 2022-08-13 瀏覽器的任務隊列-宏任務、微任務的執行順序
- 2022-04-12 error: failed to push some refs to 'git@gitlab.xxx
- 2022-05-28 利用For循環遍歷Python字典的三種方法實例_python
- 2022-12-21 Python中判斷input()輸入的數據的類型_python
- 2022-03-29 C#加密知識整合?(AES,MD5,RSA,SHA256)_C#教程
- 2022-06-29 Oracle中case?when函數的用法_oracle
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支