React内部实现cache方法示例详解
引言
前几天写的一篇介绍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
链,传参的稳定对应了链表的稳定,并最终对应了返回值的稳定。
以上就是React内部实现cache方法示例详解的详细内容,更多关于React内部实现cache方法的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341