首页 > 其他分享 >[react性能优化]--防止react-re-render: Why Suspense and how ?

[react性能优化]--防止react-re-render: Why Suspense and how ?

时间:2023-10-02 09:04:21浏览次数:392  
标签:render re react how context 组件 节点

近期内部项目基础项目依赖升级,之前使用的路由缓存不再适用,需要一个适配方案。而在此过程中react re-render算是困扰了笔者很久。后来通过多方资料查找使用了freeze解决了此问题。本文主要论述react re-render问题一般的解决方案和freeze在react内部的实现原理。react版本17.0.2

为什么会有re-render

首先re-render发生在某个react应用需要更新其状态的时候,这个状态一般分为三类

  1. 自身state发生变化
  2. 自身props发生变化
  3. 依赖的context发生变化

这三类更新一般都是正常的,是react应用完成其更新所必需要触发的,但是有部分re-render是非必需的,针对非必需的re-render是我们现在需要讨论的。

讨论之前需要多说一句的是, 对于一个合理的符合react理念编排的应用,其实re-render一般花费不了多少时间,防止re-render不能为了防止去防止

减少re-render的一般措施

  1. 父组件state变化之后,除了自身render之外,其所有子组件都会发生re-render

此处re-render的直接原因是,父组件在自己render的时候,会再一次调用:

React.createElement(Child, {props}, children)

之后返回的子节点上props发生了变化在begin work阶段无法走优化策略进而触发了re-render。

此处防止re-render的方法是用React.memo 或者使用useMemo包裹组件:

const MemoChild2 = useMemo(() => {
    return <Child2 />
  }, [])
 ...
 return <>{MemoChild2}</>
 ...

诸如此类因为父组件自身state变化而引起的re-render,还有些措施就是将state下移,即那个子组件需要这个state,就将这个state下移到该组件,避免这个子组件兄弟组件的更新

  1. 第二种是在A组件内声明B组件, 此种用法性能消耗更加吓人,根本原因是,A组件每次render函数运行之后,B都是一个新组件,对应的fiber节点上的type属性更新前跟更新后就不再指向同一组件,A组件的每一次render都会导致B组件的卸载跟挂载,根本不会存在复用:
function A() {
  const B = function() {
    useEffect(() => {
      console.log('B render')
      return () => {
        console.log('B destroy')
      }
    }, [])
    return <div>B</div>
  }
  return <>
    <div>A</div>
    <B />
  </>
}

此时若A re-render:

image.png
解决此re-render的方法就是: 将B组件移出A render函数之内:

const B = function() {
  console.log('B render')
  useEffect(() => {
    return () => {
      console.log('B destroy')
    }
  }, [])
  return <div>B</div>
}
function A() {
  console.log('A render')
  
  return <>
    <div>A</div>
    <B />
  </>
}

此时再re-render:

image.png
可以看到B没有再destroy,如果防止re-render可以参考1

  1. 组件依赖的context发生了变化

    如果组件依赖的context 发生了变化, 那么无论useMemo或者memo,都将无法起到作用。

如何防止context变化时组件的re-render

  1. 明确原则,此时的re-render是必须的,下边讨论的都是你的组件不需要re-render的时候,可以做的措施
  2. 首先你的context.provider 的value 不能在value值本身没有变化的时候而发生变化,否则子组件都会因为自身依赖的context变化而重新render,可以做的措施是useMemo等方法将value缓存起来
  3. 在2的基础上可以尝试将不同功能的context进行分割,即使用多个context
  4. 使用freeze,freeze内部其实就是suspense实现的,代码只有几行感兴趣的可以去github看看。

why and how

  1. Why?

因为已经找不到别的缓存策略来解决context发生变化时的re-render了, context变化时组件对应的updatelane为1, 会直接绕过beginwork阶段的优化策略。

  1. how?
  • Suspense 。
    一般搭配react.lazy 食用, 内部原理大体是这样的,首先 render阶段,其child会指向我们要加载的第一个组件,然后当直接child未加载成功时,beginwork阶段的react执行到child时会抛出一个错误,这个错误包含了我们的加载组件时写的那个promise,然后react在其then方法上会添加一个回调函数,用于更新Suspense. 随后,将下一个要遍历的fiber节点重置为Suspense,当begin work阶段再次执行到Suspense的时候,会在其child到sibling指向fallback,并将下一个要遍历的fiber节点置为fallback, 最后在组件加载成功时触发回调函数, 完成组件的加载。
  • 由以上的原理描述我们可以看到,Suspense在遇到抛出的异常时,是“不会管”自己的child节点的,而只是说会在child节点的sibiling上携带一个fallback节点, 那么基于此我们的child节点是可以保留之前的状态的,最重要的是,他会将下一个fiber节点置为fallback节点,因此也就绕过了我们child节点在后续的可能在begin work阶段触发re-render的机制
  • 那么freeze呢,就是根据某个属性,通过周期性的抛出异常,来避免了re-render

Suspense原理参考(https://juejin.cn/post/7145450651383201822)

标签:render,re,react,how,context,组件,节点
From: https://www.cnblogs.com/ZiLongZiLong/p/17144869.html

相关文章

  • React 18 useEffect 代码执行两次的问题
    https://github.com/zjy4fun/notes/issues/62 React18提出的新特性“并发渲染”,为了防止组件重复挂载的问题,React在开发模式&&严格模式下,useEffect会执行两次(模拟组件挂载和组件卸载,让问题提早暴露),但是线上模式不会。开发模式下,可以通过设置标志位防止useEffect执行多......
  • 《CTFshow-Web入门》10. Web 91~110
    @目录索引web91题解总结web92题解总结web93题解web94题解web95题解web96题解web97题解web98题解web99题解总结web100题解web101题解web102题解web103题解web104题解web105题解总结web106题解web107题解web108题解web109题解web110题解ctf-web入门索引web91:PHP特性之pre......
  • How Does RPC & ORM Calls Works in Odoo 16
    HowRPCWorksinOdooFramework:*Odooisanopen-sourceERP(EnterpriseResourcePlanning)frameworkthatprovidesavastrangeofbusinessapplicationfunctionalities.Itfollowsaclient-serverarchitecture,wheretheclientinteractswiththeservert......
  • Cesium加载三维模型rendering.Rendering has stopped
    使用Cesium加载数据量大、精度高的三维模型数据经常在运行一段时间后,会报如下错误:Anerroroccurredwhilerendering.Renderinghasstopped.TypeError:Failedtoexecute'shaderSource'on'WebGLRenderingContext':parameter1isnotoftype'WebGLShader'.这是由于GPU......
  • react中受控组件与非受控组件
    受控组件与非受控组件受控组件:其值由React控制的组件,通常使用state来控制和修改组件的值。例如受控的组件:classNameFormextendsReact.Component{constructor(props){super(props);this.state={value:''};}handleChange=(event)=>{thi......
  • 理解React页面渲染原理,如何优化React性能?
    ReactJSX转换成真实DOM过程当使用React编写应用程序时,可以使用JSX语法来描述用户界面的结构。JSX是一种类似于HTML的语法,但实际上它是一种JavaScript的扩展,用于定义React元素。React元素描述了我们想要在界面上看到的内容和结构。在运行React应用程序时,JSX会被转换成真实的DOM元素......
  • 前端 | 如何处理 React18 componentDidMount 重复执行两次的问题 | React
    前端|如何处理React18componentDidMount重复执行两次的问题|React问题描述按照React官网推荐方式创建项目,在运行项目的时,发现组件的componentDidMount方法被触发了两次。但是在旧项目中并没有这样的问题,于是觉得奇怪,以为是自己哪里使用错了,一直在排查。经过查阅官方文......
  • 前端 | React setState 同步异步以及处理方式 | React
    前端|ReactsetState同步异步以及处理方式|React问题描述在同步执行流程中setState表现为异步,而在异步执行流程中setState表现为同步。示例:有一个控制DOM节点显隐的状态值,默认为false,而下一步就需要获取该DOM节点做一系类处理。所以一开始使用setState设置状态值为true,让该......
  • 29、Flink SQL之DESCRIBE、EXPLAIN、USE、SHOW、LOAD、UNLOAD、SET、RESET、JAR、JOB
    Flink系列文章1、Flink部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接13、Flink的tableapi与sql的基本概念、通用api介绍及入门示例14、Flink的tableapi与sql之数据类型:内置数据类型以及它们的属性15、Flink的tableap......
  • js:创建一个基于vite 的React项目
    相关文档Vite官方中文文档React中文文档ReactRouterRedux中文文档AntDesign5.0AwesomeReact创建vite+react项目pnpmcreatevitereact-app--templatereact#根据提示,执行命令cdreact-apppnpminstallpnpmrundev项目结构$tree-L1.├──README.md├──......