我的编程空间,编程开发者的网络收藏夹
学习永远不晚

React内部实现cache方法示例详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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组件反复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链,传参的稳定对应了链表的稳定,并最终对应了返回值的稳定。

以上就是React内部实现cache方法示例详解的详细内容,更多关于React内部实现cache方法的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

React内部实现cache方法示例详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

React内部实现cache方法示例详解

这篇文章主要为大家介绍了React内部实现cache方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

react native reanimated实现动画示例详解

这篇文章主要为大家介绍了react native reanimated实现动画示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-06

React结合Drag API实现拖拽示例详解

这篇文章主要为大家介绍了React结合Drag API实现拖拽示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-06

react组件实现无缝轮播示例详解

这篇文章主要为大家介绍了react组件实现无缝轮播示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

mongose 模糊检索实现方法示例详解

目录条件查找RegExp 对象条件查找$regex为模糊查询的字符串提供正则表达式功能,MongoDB使用perl兼容正则表达式//通过条件查找,支持username模糊搜索并分页findAdminByParamsAndPageHasF
2023-08-19

React性能优化的实现方法详解

react凭借virtualDOM和diff算法拥有高效的性能,除此之外也有很多其他的方法和技巧可以进一步提升react性能,在本文中我将列举出可有效提升react性能的几种方法,帮助我们改进react代码,提升性能
2023-01-30

redis实现加锁的几种方法示例详解

前言 本文主要给大家介绍了关于redis实现加锁的几种方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。 1. redis加锁分类 redis能用的的加锁命令分表是INCR、SETNX、SET2. 第一种锁命令INCR
2022-06-04

React实现数字滚动组件numbers-scroll的示例详解

数字滚动组件,也可以叫数字轮播组件,这个名字一听就是非常普通常见的组件。本文将利用React实现这一组件,感兴趣的小伙伴可以了解一下
2023-03-10

GoJava算法之外观数列实现方法示例详解

这篇文章主要为大家介绍了GoJava算法外观数列实现的方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

React文件分段上传实现方法详解

这篇文章主要介绍了React文件分段上传实现方法,将文件切成多个小的文件;将切片并行上传;所有切片上传完成后,服务器端进行切片合成;当分片上传失败,可以在重新上传时进行判断,只上传上次失败的部分实现断点续传;当切片合成为完整的文件,通知客户端上传成功
2022-11-13

深入理解Golang方法的内部实现

Golang是由Google开发的一种静态类型的编程语言,以其简洁的语法和高效的性能而备受程序员欢迎。在Golang中,方法是一种特殊的函数,用于为结构体添加行为。本文将深入探讨Golang方法的内部实现,通过具体的代码示例帮助读者更好地理
深入理解Golang方法的内部实现
2024-02-23

JavaScript实现LRU算法的示例详解

不知道屏幕前的朋友们,有没有和我一样,觉得LRU算法原理很容易理解,实现起来却很复杂。所以本文就为大家整理了一下实现的示例代码,需要的可以参考一下
2023-05-17

Android App实现应用内部自动更新的最基本方法示例

这只是初步的实现,并没有加入自动编译等功能。需要手动更改更新的xml文件和最新的apk。 共涉及到四个文件! 一、客户端 AndroidUpdateTestActivity:程序首页 main.xml:首页布局 Update:更新类
2022-06-06

Python实现内存泄露排查的示例详解

一般在python代码块的调试过程中会使用memory-profiler、filprofiler、objgraph等三种方式进行辅助分析,今天这里主要介绍使用objgraph对象提供的函数接口来进行内存泄露的分析,感兴趣的可以了解一下
2023-01-28

编程热搜

目录