React内部是如何实现cache方法的?

2年前 (2022-11-17)阅读3回复1
小小的人啊
小小的人啊
  • 管理员
  • 注册排名4
  • 经验值473850
  • 级别管理员
  • 主题94770
  • 回复0
楼主

存眷

“脚本之家

”,与百万开发者在一路

出处:魔术师卡颂(ID:gh_52d0bec584f9)

如若转载请联络原公家号

前几天写的一篇介绍 use 那个新 hook 的文章中聊到 React 原生实现了一个缓存函数的办法 —— cache 。

关于如下代码,被 cache 包裹的函数,当屡次挪用时,若是传参稳定,会始末返回缓存值:

constcacheFn = cache(fn);

cacheFn( 1, 2, 3);

// 不会施行fn,间接返回缓存值

cacheFn( 1, 2, 3);

React 内为什么需要 cache 办法呢?考虑如下组件:

constfetch = cache(fetchUserData);

functionUser( {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行代码,起首我们来阐发下实现要点。

若是参数稳定,则利用缓存的值。那意味着我们需要处置:

参数的挨次

举个例子,当参数挨次变了,不利用缓存值:

constcacheFn = cache(fn);

cacheFn( 1, 2, 3);

// 不利用缓存值

cacheFn( 3, 2, 1);

区别处置引用类型、原始类型参数

举个例子,当统一位置的参数传递了统一个引用类型值,则返回缓存值:

constcacheFn = cache(fn);

constobj = {};

cacheFn( 1, obj, 3);

// 返回缓存值

cacheFn( 1, obj, 3);

当统一位置的参数传递了差别引用类型值,则不返回缓存值:

constcacheFn = cache(fn);

constobj = {};

cacheFn( 1, obj, 3);

// 不返回缓存值

cacheFn( 1, {}, 3);

缓存的垃圾收受接管

缓存数据时,要留意 「缓存失效但是引用的数据没有释放」形成的内存泄露问题。

所以,关于引用类型数据,能够利用 WeakMap 保留。

关于原始类型数据,能够利用 Map 保留。

WeakMap 与 Map 的区别在于 —— 在 WeakMap 中, key 到他对应的 value 是弱引用。那意味着当没有其他数据引用那个 key 时,他能够被垃圾收受接管。而在 Map 中, key 到 value 是强引用,即便没有其他数据引用那个 key ,他也不会被垃圾收受接管。

实现原理

本文不会介绍详细的代码实现(大段贴代码让人看起来头疼)。

我会用示例图讲解实现原理。领会原理后,若是你对实现细节感兴趣,能够参考:

cache的源码实现PR[1]

cache的在线示例[2]

cache的源码实现PR[1]

cache的在线示例[2]

关于如下代码:

constcacheFn = cache(fn);

constobj = {};

cacheFn( 1, obj, 3);

cacheFn 的每个传参,对应 cache 内部的一个 cacheNode 节点:

// CacheNode构造函数

functioncreateCacheNode T( ): CacheNode T {

return{

s: UNTERMINATED,

v: undefined,

o: null,

p: null

字段的意义如下:

s: cacheNode 的缓存形态,有 未中行/中行/发作错误 3种形态

v: cacheNode 缓存的值

o:缓存的引用类型值

p:缓存的原始类型值

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 链,传参的不变对应了链表的不变,并最末对应了返回值的不变。

参考材料

[1]

cache的源码实现PR:

[2]

cache的在线示例:

END

法式员专属卫衣

商品曲购链接

【☝🏼点击查看更多详情】

专属定造,法式员秒懂的极客卫衣!

React:搞了半天,我才是低代码的更佳形态

React团队是若何测试并发特征的

全网更优雅的 React 源码调试体例

Vue 性能起头反超 React 了?

Office 2019/2021专业加强版,正版末身受权!

0
回帖

React内部是如何实现cache方法的? 相关回复(1)

风轻云淡
风轻云淡
沙发
React内部通过高效的数据结构和算法实现cache方法,优化性能。
传说2个月前 (06-09 06:32)回复00
取消
载入表情清单……
载入颜色清单……
插入网络图片

取消确定

图片上传中
编辑器信息
提示信息