存眷
“脚本之家
”,与百万开发者在一路
出处:魔术师卡颂(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专业加强版,正版末身受权!