[react]10、react性能优化

1、列表&key

一、React更新流程

React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树。React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI。

  • 同层节点之间相互比较,不会垮节点比较;
  • 不同类型的节点,产生不同的树结构;
  • 开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定;

1、对比不同类型的元素
当节点为不同的元素,React会拆卸原有的树,并且建立起新的树

<div>
  <Child />
</div>
~~~~~~~~~~~
<span>
  <Child />
</span>

2、对比同一类型的元素
当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性
1)、React 知道只需要修改 DOM 元素上的 className 属性

<div classname="before" title="stuff" />
<div classname="after" title="stuff" />

2)、当更新 style 属性时,React 仅更新有所更变的属性

<div style={{ color: 'red', fontWeight: 'bold' }} />
<div style={{ color: 'green', fontWeight: 'bold' }} />

3)、同类型的组件元素
组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps() 和 componentWillUpdate() 方

3、对子节点进行递归
在默认条件下,当递归 DOM 节点的子元素时,React 会同 时遍历两个子元素的列表;当产生差异时,生成一个 mutation。
1)、在最后插入一条数据的情况,前面两个比较是完全相同的,所以不会产生mutation,最后一个比较,产生一个mutation,将其插入到新的 DOM树中即可;

<ul>
  <li>first</li>
  <li>second</li>
</ul>
~~~~~~~~~
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

最后一次插入third,只会产生一个mutation
2)、如果我们是在中间插入一条数据
React会对每一个子元素产生一个mutation,这种低效的比较方式会带来一定的性能问题

<ul>
  <li>first</li>
  <li>second</li>
</ul>
~~~~~~~~~
<ul>
  <li>zero</li>
  <li>first</li>
  <li>second</li>
</ul>

在第一次插入zero,后面first、second都会产生mutation。

二、keys的优化

我们在前面遍历列表时,总是会提示一个警告,让我们加入一个key属性

  • 在最后位置插入数据,这种情况,有无key意义并不大
  • 在前面插入数据,这种做法,在没有key的情况下,所有的li都需要进行修改
  • 当子元素(这里的li)拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素
    key的注意事项:
    1)、key应该是唯一的;
    2)、key不要使用随机数(随机数在下一次render时,会重新生成一个数字);
    3)、使用index作为key,对性能是没有优化的;

2、 shouldComponentUpdate

在render调用之前会调用shouldComponentUpdate,不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。
该方法有两个参数:

  • nextProps 修改之后,最新的props属性
  • nextState 修改之后,最新的state属性

该方法返回值是一个boolean类型

  • 返回值为true(默认返回true),那么就需要调用render方法
  • 返回值为false,那么就不需要调用render方法
shouldComponentUpdate(nextProps, nextState) {
   if (this.state.counter !== nextState.counter) {
        return true;
   }
   return false;
}

源码分析

// react-reconciler/src/forks/ReactFiberClassComponent.new.js Line 291
function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextContext,
) {
  const instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') { // 判断instance有无shouldComponentUpdate方法
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextContext,
    );
    return shouldUpdate;
  }
  
  // 如果isPureReactComponent=true就会进行浅层比较
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }
  return true;
}

3、PureComponent

React.PureComponentReact.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。

import React, { Component, PureComponent } from 'react'
// header
class Header extends PureComponent {
    render() {
        console.log('App Header被调用')
        return (
            <h2>我是Header组件</h2>
        )
    }
}

export class App extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {counter: 0}
    }
    render() {
        console.log('App 被调用')
        return (
            <div>
                <h2>{"当前计数:" + this.state.counter}</h2>
                <button onClick={e => this.increment()}>+1</button>
                <Header/>
            </div>
        )
    }
    increment () {
        this.setState({
            counter: this.state.counter + 1
        })
    }
}
export default App

源码分析

PureComponent源码

// react/src/ReactBaseClasses.js Line 129
/**
 * Convenience component with default shallow equality check for sCU.
 */
function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// isPureReactComponent标记为true
pureComponentPrototype.isPureReactComponent = true; 

export {Component, PureComponent};

shallowEqual源码

// shared/shallowEqual
/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) { // 如果是同一个对象,直接返回true
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return true;
}

4、memo

React.memo 为高阶组件。
如果组件在相同 props 的情况下渲染相同的结果,那么可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。
React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中拥有 useStateuseReduceruseContext 的 Hook,当 state 或 context 发生变化时,它仍会重新渲染。
默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);

源码分析

memo函数入口

// react/src/ReactMemo.js Line 12
export function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  // 调用compare方法
  const elementType = {
    $$typeof: REACT_MEMO_TYPE, // memo函数的标志
    type,
    compare: compare === undefined ? null : compare,
  };
  return elementType;
}

updateMemoComponent:使用memo函数实现的类会调用此函数

// react-reconciler/src/forks/ReactFiberBeginWork.new.js Line 384
function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateLanes: Lanes,
  renderLanes: Lanes,
): null | Fiber {
  if (current === null) {
    const type = Component.type;
    if (
      isSimpleFunctionComponent(type) &&
      Component.compare === null &&
      // SimpleMemoComponent codepath doesn't resolve outer props either.
      Component.defaultProps === undefined
    ) {
      let resolvedType = type;
      // If this is a plain function component without default props,
      // and with only the default shallow comparison, we upgrade it
      // to a SimpleMemoComponent to allow fast path updates.
      workInProgress.tag = SimpleMemoComponent;
      workInProgress.type = resolvedType;
      return updateSimpleMemoComponent(
        current,
        workInProgress,
        resolvedType,
        nextProps,
        updateLanes,
        renderLanes,
      );
    }
    const child = createFiberFromTypeAndProps(
      Component.type,
      null,
      nextProps,
      workInProgress,
      workInProgress.mode,
      renderLanes,
    );
    child.ref = workInProgress.ref;
    child.return = workInProgress;
    workInProgress.child = child;
    return child;
  }
  const currentChild = ((current.child: any): Fiber); // This is always exactly one child
  if (!includesSomeLane(updateLanes, renderLanes)) {
    // This will be the props with resolved defaultProps,
    // unlike current.memoizedProps which will be the unresolved ones.
    const prevProps = currentChild.memoizedProps;
    // Default to shallow comparison
    // 判断compare是否存在,决定使用compare还是shallowEqual
    let compare = Component.compare;
    compare = compare !== null ? compare : shallowEqual;
    if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    }
  }
  // React DevTools reads this flag.
  workInProgress.flags |= PerformedWork;
  const newChild = createWorkInProgress(currentChild, nextProps);
  newChild.ref = workInProgress.ref;
  newChild.return = workInProgress;
  workInProgress.child = newChild;
  return newChild;
}


喜欢的朋友记得点赞、收藏、关注哦!!!

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

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

相关文章

Android 托管 Github Action 发布 Github Packages ,实现 Mvn 免费自动化托管

自从多年前 JCenter 关闭服务之后&#xff0c;GSY 项目版本就一直发布在 Jitpack 上&#xff0c;如今每个月也都有大概 10w 左右下载&#xff0c;但是近年来时不时就会出现历史版本丢失的问题&#xff0c;而且有时候还不是某个具体版本丢失&#xff0c;而是版本里的某几个依赖突…

基于Javaweb+MySQL实现学生选课系统

学生选课系统设计报告 一、阶段&#xff1a;开发前的设置和思考 1.1 题目要求&#xff1a; 建立一个学生选课系统, 编写应用程序完成系统开发。 建立基本表&#xff1a; 学生&#xff08;学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;系别&#xff0…

centos7 安装python3.9.4,解决import ssl异常

本篇文章介绍如何在centos7中安装python3.9.4(下文简称python3)&#xff0c;解决python3安装后import ssl模块失败问题&#xff0c;为什么我要在centos7中安装python呢&#xff0c;因为我需要在服务器中跑python数据处理脚本。 安装python3同时解决import ssl模块失败问题总共包…

Springboot 整合 Java DL4J 实现情感分析系统

&#x1f9d1; 博主简介&#xff1a;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;…

计算机网络串联——打开网站的具体步骤

参与浏览网站的重要名词 1、MAC地址也叫硬件地址、物理地址。那这个东西就是设备你出厂的时候就会带有的&#xff0c;详情跳转&#xff0c;其构成如下&#xff1a; MAC地址用16进制表示&#xff0c;就是一共有12位&#xff0c;前六位他们会像一个国际的这种专门组织去申…

<项目代码>YOLOv8 夜间车辆识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

xilinx vitis 更换硬件平台——ZYNQ学习笔记5

1、重新生成硬件信息 2、选择带有bit信息 3、设施路径和名字 4、打开更新硬件选项 5、选择新的硬件信息 6、打开系统工程界面 7、复位硬件信息 更新完毕

【温酒笔记】SPI

1. SPI基础 物理层 片选线 &#xff1a;选中拉低SCK: 时钟线MOSI:主出从入MISO:主入从出 协议层 CPOL:时钟极性&#xff1a;空闲电平高低 CPHA:时钟相位&#xff1a;第一个还是第二个边沿采样 2. 示例SPI-W25Q16 (见模组分类下文章)

Web应用性能测试工具 - httpstat

在数字化时代&#xff0c;网站的性能直接影响用户体验和业务成功。你是否曾经在浏览网页时&#xff0c;遇到加载缓慢的困扰&#xff1f;在这个快速变化的互联网环境中&#xff0c;如何快速诊断和优化Web应用的性能呢&#xff1f;今天&#xff0c;我们将探讨一个强大的工具——h…

Transformer 论文精读与完整代码复现【Attention Is All You Need】

本文将在一开始快速概括模型的主体&#xff0c;论文的贡献以及重要图表&#xff0c;然后使用 PyTorch 复现完整且与原论文对齐的 Transformer&#xff08;位于后半段&#xff09;。通过阅读文章能够了解&#xff1a; 单头与多头的区别&#xff1f;掩码到底掩了什么&#xff1f;…

element-plus按需引入报错AutoImport is not a function

官网文档&#xff1a;快速开始 | Element Plus webpack配置 // webpack.config.js const AutoImport require(unplugin-auto-import/webpack) const Components require(unplugin-vue-components/webpack) const { ElementPlusResolver } require(unplugin-vue-components…

《Python网络安全项目实战》项目2 Python基础练习_总复习(2)

《Python网络安全项目实战》项目2 Python基础练习_总复习&#xff08;2&#xff09; 期末总复习&#xff08;2&#xff09; 班级: 姓名: 实训成绩: 任务单成绩: 利用%d&#xff0c;%f占位符格式化输出。 Name “小明” age 16 height 1.7355774 ___________________________…

自扶正救生艇,保障水上救援的安全卫士_鼎跃安全

在应急事件中&#xff0c;自扶正救生艇能够发挥关键的救援和保障作用&#xff0c;确保救援人员和被困人员的生命安全&#xff0c;尤其在极端天气或突发水上事故中展现出明显优势。 在救援过程中如果遭遇翻船&#xff0c;救生艇能够迅速恢复正常姿态&#xff0c;确保救援人员不会…

SAR_ADC介绍和建模

SAR-ADC介绍以及建模 一、介绍 1、工作原理 采用多个时钟周期&#xff0c;将DAC的模拟输出VDAC逐次逼近此采样模拟输入&#xff0c;获得数字输出结果。采用二进制搜索或者折半查找算法 2、工作过程 1&#xff1a;信号与VREF/2相比&#xff0c;结‘1&#xff0c;代表信号>…

什么是 OpenTelemetry?

OpenTelemetry 定义 OpenTelemetry (OTel) 是一个开源可观测性框架&#xff0c;允许开发团队以单一、统一的格式生成、处理和传输遥测数据&#xff08;telemetry data&#xff09;。它由云原生计算基金会 (CNCF) 开发&#xff0c;旨在提供标准化协议和工具&#xff0c;用于收集…

Nginx 实现动态封禁IP,详细教程来了

Nginx 实现动态封禁IP&#xff0c;详细教程来了 需求环境准备设计方案在操作系统层面&#xff0c;配置 iptables&#xff0c;来拦截指定 IP 的网络请求在 Web 服务器层面&#xff0c;通过 Nginx 自身的 deny 选项或者 lua 插件配置 IP 黑名单在应用层面&#xff0c;在处理请求之…

11月3日笔记(根据凭据提权)

用户凭据操作 枚举 Unattended 凭据 无人值守(Unattended)安装允许应用程序在不需要管理员关注下自动安装。无人值守安装的问题是会在系统中残留一些配置文件&#xff0c;其中可能包含本地管理员的用户名和密码&#xff0c;常见的路径如下。 C:\sysprep.inf C:\syspreg\sysp…

如何解决permission denied - invalid PVE ticket (401)

PVE8.2访问出现&#xff1a;permission denied - invalid PVE ticket (401)&#xff0c;解决方法: 1、重启动PVE&#xff0c;然后登录。 去浏览器设置页面&#xff0c;清空cokie&#xff0c;然后再登录。如果问题仍然存在&#xff0c;就按下面的方法进一步确定。 2、另外一种…

驱动——线程断链和信息获取

实验环境&#xff1a;win7 x32 断链&#xff1a; #include <ntifs.h>NTSTATUS EnumThread(ULONG ulPid, ULONG ulTid) {PEPROCESS pProcessAddr PsGetCurrentProcess();PLIST_ENTRY pHeadlink (PLIST_ENTRY)((ULONG)pProcessAddr 0xb8);PLIST_ENTRY pNextlink pHead…

打羽毛球为什么要在气膜馆?—轻空间

在现代健身环境中&#xff0c;羽毛球作为一项受欢迎的运动&#xff0c;不仅能够锻炼身体&#xff0c;还能增强社交互动。选择在气膜馆打羽毛球&#xff0c;能为运动爱好者带来全新的体验和诸多优势。 优越的空间设计 气膜馆的最大特点是其独特的空间设计。与传统的体育馆相比&a…