日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React內部實現cache方法示例詳解_React

作者:卡頌 ? 更新時間: 2022-12-09 編程語言

引言

前幾天寫的一篇介紹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組件反復renderfetch(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保存。

WeakMapMap的區別在于 —— 在WeakMap中,key到他對應的value是弱引用。這意味著當沒有其他數據引用這個key時,他可以被垃圾回收。而在Map中,keyvalue是強引用,即使沒有其他數據引用這個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

欄目分類
最近更新