没想到?React 编译器还可以玩这个?!

🔥🔥🔥 前方高能,干货满满,建议点赞➕关注➕收藏;

React 19 和 React 编译器(此前称作React Forget)最近一个月成为了 React 社区热议的焦点。大家都对于可能很快就不必再在 React 中纠结于记忆化技术(memoization) 的问题感到异常激动(这是件好事)。但这种说法准确吗?在未来几个月内,我们真的可以开始抛弃 memouseMemouseCallback这些概念吗?而当 React 编译器正式推出后,又会带来哪些实质性的变化?我们又应该如何学习 React 的新知识呢?

我们来深入探讨一下。

React 19 不是 React 编译器

让我们首先澄清一个最关键的误区:记忆化技术(Memoization)在短期内仍将是 React 开发的重要部分,因此现在还不是抛弃它的时候。需要明确的是,React 19 和 React 编译器是两件不同的事物。React 团队在他们宣布即将发布 React 19 的同一篇博文中提到了编译器,这让许多人误以为二者是相同的,误解纷纷产生。

不过,React 团队的一位成员通过一条推文对这个误解进行了澄清,他说明了React 19 与 React Compiler之间的区别。这条推文帮助大家理解了 React 的最新动态,避免了进一步的混淆

p1.webp

在 React 19 版本中,我们期待引入多项新功能,但对于 React 编译器的推出,则需要再耐心等待一段时间。目前尚不明确具体需要等待多久,但根据 React 核心团队另一位成员的推文透露,编译器可能在本年度末前推出。这一消息让我们对 React 的未来发展充满期待。

p2.webp

就我个人而言,我对这个时间表持怀疑态度。 如果我们看一下 React 团队成员介绍编译器及其时间表的演讲,我们正处于编译器之旅的中间位置:

p3.webp

这段开发之旅始于2021年,已经两年了。在像 Meta 这样的庞大代码库中实施这种基础性的变革无疑是一项巨大的挑战,从时间线的中段跳跃到最终实现可能还需要再多两年的时间。

不过,谁能确切知晓呢?也许 React 团队真能在今年完成发布,那无疑是个振奋人心的好消息。根据视频中的介绍,Compiler 一个主要的能力是我们在采用它时无需修改现有代码——它将会“即插即用”。如果 Compiler 确实能在年底前发布,那将强有力地证明这一点,我们大多数人将能够迅速、轻松地进行切换。

然而,即便 Compiler 今年发布,并且确实非常容易的使用而且无任何副作用,这并不意味着我们可以立刻忘掉 useCallback和 memo 的使用。总会有一个过渡期,在这个期间,我们最初会讨论 「如果你已经启用了 Compiler」 的情况,然后逐渐过渡到「如果你还没有迁移到 Compiler」的较为少见的情况。

从类组件转向使用 hooks 的函数组件,这一心智模式的转变我认为至少需要 3 年时间(从2018年起)——当所有教程、文档和博客文章都更新之后,大多数人转向了使用 hooks 的 React 版本,并且我们开始默认讨论函数组件和 hooks。即使在 6 年后的今天,我们仍然可以在不同的地方找到许多类组件。

如果对 Compiler 我们采取相似的时间线预测,那意味着我们至少在未来 3 年内还需要保持对memouseMemo和 useCallback 这些知识的掌握。如果你足够幸运能够在 Compiler 一发布就迁移到一个现代化的代码库,那么你可能需要较短的时间来适应。但如果你是 React 的教学者,或者在一个迁移速度较慢、充斥着大量旧代码的大型代码库中工作,那么你可能需要更长的时间来适应。

React Compiler带来了什么变化?

所以,React Compiler 究竟会带来什么样的改变呢?简而言之,它将实现代码的全面记忆化(memoization)处理。具体来说,React Compiler 是一个 Babel 插件,这意味着它能够自动将我们编写的标准 React 代码转化为一种新的形式。在这种形式中,无论是组件内部使用的钩子(hooks)的依赖关系,还是组件接收的属性(props),乃至组件本身,都将经过记忆化处理。这种处理方式能显著优化性能,因为它通过避免不必要的计算和渲染来提高应用的响应速度和效率。

通过这种转换,原本的 React 代码将被优化,以确保应用中的数据和组件在不必要更新时能够保持不变,从而减少性能损耗。这个过程是自动进行的,开发者不需要手动对每个组件或钩子进行记忆化操作,React Compiler 为我们智能地处理了这一切。

const Component = () => {
  const onSubmit = () => {};
  const onMount = () => {};

  useEffect(() => {
    onMount();
  }, [onMount]);

  return <Form onSubmit={onSubmit} />;
};

就像下面,onSubmit 和 onMount 函数都经过了useCallback的包裹处理,同时 Form 组件也被React.memo包裹

const FormMemo = React.memo(Form);

const Component = () => {
  const onSubmit = useCallback(() => {}, []);
  const onMount = useCallback(() => {}, []);

  useEffect(() => {
    onMount();
  }, [onMount]);

  return <FormMemo onSubmit={onSubmit} />;
};

当然,Compiler 的工作原理并不是直接将代码转换成我们上述所描述的形式;实际上,它的操作更为复杂和先进。然而,将这种转换过程想象为函数和组件通过 useCallback 和 React. memo进行包裹,这样的思维模型有助于我们更好地理解 Compiler 的作用。

如果你对 Compiler 的具体工作机制感兴趣,我推荐你观看 React 核心团队成员介绍 Compiler 的视频。此外,如果你对为何在这些场景中使用 useCallback 和 memo 还有所疑惑,我建议你观看油管上的《the Advanced React series》 前六集,它们全面讲解了组件重新渲染和记忆化的相关知识。如果你更喜欢阅读,那么这些文档内容都是不容错过的学习资源。

对于 React 教学和学习方法来说,这种技术转变带来了一些新的考量。这意味着在理解和应用 React 的过程中,我们需要更新我们的知识库,同时也提供了一个机会去深入探索 React 的性能优化技巧,让我们能够更有效地构建和优化我们的 React 应用。

父子组件重新渲染

目前,如果父组件重新渲染,则里面所有的子组件也会重新渲染

// 如果 Parent 重新渲染
const Parent = () => {
  // Child 也会重新渲染
  return <Child />;
};

当前,很多人坚信只有当子组件的属性(props)发生变化时,组件才会进行重新渲染。我会把这个观念称为「重新渲染神话」。实际上,在React的标准行为模式下,这种说法并不成立——属性的变化并不总是导致组件的重新渲染。

然而,引入 Compiler 后,情况发生了有趣的转变。得益于 Compiler 在底层实现的全面记忆化处理,这个曾被认为是神话的观念现在反倒成了 React 的常态。在未来几年,我们将会向学习者传授这样的理念:一个 React 组件仅在其状态或属性发生变化时才会重新渲染,而无论其父组件是否进行了重新渲染都不会影响到它。有时,技术的发展确实会带来一些意想不到的变化和趣味。

不再为了性能而组合

以往,像 “向下移动状态” 或 “通过子组件传递” 这样的技术被广泛用于减少不必要的组件重新渲染,以此来提升应用的性能。我建议在尝试用 useCallback 和 memo 来手动优化之前,可以先考虑这些组合技术,因为在 React 中恰当地实现记忆化(即缓存组件以避免不必要的更新)是一件非常具有挑战性的事情。

比如,像下面的代码

const Component = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        open dialog
      </Button>
      {isOpen && <ModalDialog />}
      <VerySlowComponent />
    </>
  );
};

由于 ModalDialog 组件是延迟打开的,所以 VerySlowComponent 会在每次 ModalDialog 打开时都重新渲染一次。如果我们把 state 放进 ModalDialog 组件内,像下面这样

const ButtonWithDialog = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        open dialog
      </Button>
      {isOpen && <ModalDialog />}
    </>
  );
};

const Component = () => {
  return (
    <>
      <ButtonWithDialog />
      <VerySlowComponent />
    </>
  );
};

通过这种方式,我们成功地避免了VerySlowComponent不必要的重新渲染,而且这个过程中我们甚至没有使用到任何记忆化技术。

随着 React Compiler 的推出,那些过去为了提升性能而必须采用的编码模式将不再是必需的。尽管如此,出于代码组织和关注点分离的考虑,我们可能仍会采用这些模式。但是,过去那种迫使我们将大型组件拆分为更小组件以避免不必要的重新渲染的驱动力将不复存在。在React Compiler的帮助下,我们的组件可以变得更加庞大而不会带来性能上的负担。

这表明,React 开发者可以在不牺牲性能的前提下,拥有更大的灵活性来组织和设计他们的组件结构。这一转变为 React 应用的开发带来了新的可能性,使得性能优化不再是开发过程中的一个繁重负担。

不再到处都是 useMemo/useCallback

自然而然,那些有时候让我们的代码变得复杂的 useMemo 和 useCallback 将会消失。这部分让我最为期待。不必再费劲穿梭于多层组件之间,只为了缓存一个 onSubmit 属性的回调函数。不再有那些难以阅读和调试的,互相依赖又让人费解的 useMemo 和 useCallback 链条。也不会再因为忘记缓存子组件而导致缓存失效的问题。

差异比对和协调过程

我们或许需要重新思考,如何向人们解释 React 中的差异比对(diffing)和协调过程(reconciliation)。目前简化说法是,当我们渲染一个组件,比如 <Child /> 时,实际上我们是在创建它的一个元素对象。这个元素对象大概是下面这样

{
  "type": ...,
  "props": ...,
  // 其他 react 属性
}

“type” 是字符串或者组件的引用

在下面的代码中

const Parent = () => {
  return <Child />;
};

当父组件(Parent)发生重渲染时,函数会被触发,同时<Child />组件的对象会被重新生成。React 会在重渲染之前和之后对这个对象进行一次浅层对比。如果这个对象的引用发生了变化,就意味着 React 需要对这个子组件树执行一次全面的差异分析。

目前,即便 <Child /> *组件没有接收任何属性(props),它还是会不断地重新渲染,原因就在于<Child />(实际上是对 React.createElement* 函数调用的简写)的结果是一个总是被重新创建的对象,因而它无法通过浅层对比的检查。

随着 React 编译器的引入,元素(Elements)、差异对比(diffing)和协调过程(reconciliation)的基本概念仍然保持不变,这是件好事。但变化的是,现在如果<Child ****/>组件的属性没有变化,它将返回一个已经被缓存(memoized)的对象。因此,编译器的引入实际上相当于将所有内容,包括组件元素,都使用了useMemo 进行了包裹和优化。

const Parent = () => {
  const child = useMemo(() => <Child />, []);
  return child;
};

不过,这只是我基于目前能公开获取的有限信息所作出的一些推测,所以我的看法可能并不完全准确。不过,这些细节对我们的生产代码来说并没有太大的实际影响。

其他方面几乎和目前的情况一样。在一个组件内部创建另一个组件,这种做法仍然是一个明显的设计误区。我们还是会用 “key” 属性来识别元素或者重置状态。处理 Context 仍然是一个棘手的问题。至于数据获取或错误处理等话题,这些目前甚至还没有被纳入讨论范围。

但话说回来,我真的很期待编译器的发布。它似乎将为我们的 React 编程带来重大的改进。

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/508221.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

备战蓝桥杯Day36 - 动态规划 - 三角形最小路径和问题

一、什么是动态规划 通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推的方式解决。 哪些问题可以使用动态规划&#xff1f; 1、具有最优子结构&#xff1a;问题的最优解所包含的子结构的解也是最优的 2、具有无后效性&#xff1a;未来…

爬取BOSS直聘招聘数据(详情页数据+__zp_stoken__逆向)

这里携带逆向方法进行请求 获得数据 需要逆向方法请私聊 , 下面部分只展示爬取思路 对网页进行分析抓包 设置参数 – 城市/薪资范围/职业 对网页进行请求获得数据集 利用xpath,soup等进行进行数据清洗 将数据一csv的格式保存

稳定性生产总结

本期我们来谈下稳定性生产这个话题&#xff0c;稳定性建设目标有两个&#xff1a;降发生、降影响&#xff0c; 在降发生中的措施是做到三点&#xff1a;系统高可用、 高性能、 高质量&#xff0c;三高问题确实是一个很热的话题&#xff0c;里面涉及很多点。 在降影响中要做到…

Express.js项目实战(1)—— 我的藏书馆

首先新建文件夹——myLibrary 在vscode中点击文件>点击 Duplicate Workspace(以工作区的方式打开文件夹myLibrary) 点击duplicate Workspace&#xff08;打开工作区&#xff09; 之后&#xff0c;会出现以下界面 点击打开文件夹&#xff0c;选择新建的文件夹&#xff0c;会出…

小黑逆向爬虫探索与成长之路:小黑独立破解毛毛租数据加密与解密

前言 有道和招标网的加密入口定位在前面两期做了详细的介绍&#xff0c;本小结将通过简单的关键词搜索定位到加密与解密入口 数据接口寻找与请求 根据响应数据长度&#xff0c;确定数据接口&#xff0c;发现传入的参数需要加密&#xff0c;响应的结果需要解密&#xff0c;后…

为什么鸿蒙系统那么火,就业岗位却很少?而且很少有公司愿意培养新人?

近期某乎上有这么一则问答提问&#xff1a;“为什么鸿蒙系统那么火,就业岗位却很少?而且很少有公司愿意培养新人?” 都说2024是原生鸿蒙的关键一年&#xff0c;华为鸿蒙各种大动作也没有停过。根据智联招聘数据显示&#xff0c;2023年9月-12月&#xff0c;鸿蒙相关职位数同比…

【Linux入门】Linux简史

Linux 是什么&#xff1f;Linux 是一款叫做操作系统的软件。 操作系统这款软件有什么样的意义呢&#xff1f;简单来说&#xff0c;比如有顾客买了一台笔记本电脑&#xff0c;这台笔记本电脑由电脑硬件组成&#xff0c;在这堆硬件上一定搭载了一款操作系统。正因为操作系统存在&…

【Unity每日一记】这些时间成员变量你是否清楚(Timescale,Time.deltaTime,Time.unscaledDeltaTime等)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

dockerfile制作-pytoch+深度学习环境版

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 文档内容docker相关术语docker常用命令容器常用命令根据dockerfile创建容器dokerfile文件内容 docker问题&#xff1a;可能的原因和解决方法示例修改修改后的D…

C++笔记:命名空间

引入&#xff1a; 平常&#xff0c;我们在进行C编写时&#xff0c;一般我们都会默认在开始去写这样的代码&#xff1a; #include<iostream>//包含头文件using namespace std;//展开命名空间 这里就出现了与C语言不同的地方&#xff1a;这里的命名空间就是C对于C语言进…

Linux:Patch补丁、Diff使用

what的问题 diff命令&#xff0c;记录两个文件的差别&#xff0c;通过diff得到一个patch文件&#xff0c;也应用patch到另外一个文件&#xff0c;通过patch命令 diff and patch are intended to be used on text files. why的问题 Reason 1: diff can be useful by itself t…

如何实现多个PDF文件合并为一个PDF文件

公众号&#xff1a;程序员白特&#xff0c;欢迎一起交流学习~ hi&#xff0c;我是白特。 最近看到一个功能&#xff0c;十分感兴趣&#xff0c;也就是我们要将多个文件服务器中的PDF文件合并为一个PDF文件并以此进行下载打印操作。 那么直接让我们一起看下它的实现思路吧。 …

OpenHarmony实战:硬件适配之HCS应用

一、HCS 配置管理 HCS(HDF Configuration Source)是 HDF 驱动框架的配置描述参数文件&#xff0c;内容以 Key-Value 为主要形式。它实现了配置代码与驱动代码解耦&#xff0c;便于开发者进行配置管理。 HC-GEN(HDF Configuration Generator)是 HCS 配置转换工具&#xff0c;可…

Git、TortoiseGit、SVN、TortoiseSVN 的关系和区别

Git、TortoiseGit、SVN、TortoiseSVN 的关系和区别 &#xff08;二&#xff09;Git&#xff08;分布式版本控制系统&#xff09;:&#xff08;二&#xff09;SVN&#xff08;集中式版本控制系统&#xff09;&#xff08;三&#xff09;TortoiseGit一、下载安装 git二、安装过程…

“转行做程序员”很难?这里有4个建议

近几年来&#xff0c;传统行业多处于经济下行&#xff0c;加上互联网行业的赚钱效应&#xff0c;想要转行到这一行的人越来越多&#xff0c;其中程序员这个行业更是很多人梦寐以求的。 但另一方面&#xff0c;我们也发现&#xff0c;这些想要转行的同学们往往会遇到很多困扰。…

企业员工在线培训系统功能介绍

随着信息技术的飞速发展&#xff0c;企业员工培训方式正逐步从传统的面授转向灵活高效的在线培训。一个综合性的企业员工在线培训系统能够为员工提供多样化的学习资源、便捷的学习途径和有效的学习监督&#xff0c;以下是该系统的主要功能详细介绍&#xff1a; 1. 课程功能 线…

如何应对光模块故障,只需一条命令!

你们好&#xff0c;我的网工朋友。 是设备就有故障&#xff0c;光模块也不例外&#xff0c;而且很多项目的故障首先要排除光模块的问题。 像光模块型号选用是否正确&#xff1f; 使用的跳线是否正确&#xff1f; 交换机接口是否用匹配&#xff1f; ....各种各样的问题&…

MySQL中count(*) 和 count(1)区别

MySQL 中 count(*) 和 count(1) 的异同 count() 函数的基本原理 语法&#xff1a; COUNT(expr)其中&#xff1a; expr 可以是字段名、常量、表达式或星号 (*)。 用法&#xff1a; count() 函数用于统计满足特定条件的记录数量。它可以有以下几种用法&#xff1a; 1. 统计…

【带你了解下前端开发语言有那些】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

JavaEE初阶之线程安全(一)

目录 题外话 正题 1.线程调度是随机的 2.修改共享数据 知识点 线程同步机制 线程异步机制 举例说明 synchronized() 知识点 举例说明 举例代码详解 死锁 举个例子: 代码 小结 题外话 这两天忽冷忽热的感冒了,昨天状态特别不好断更了一天,今天继续加油! 我会把…