IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React性能优化 -> 正文阅读

[JavaScript知识库]React性能优化

React性能优化

1、React性能优化方向

  • 减少重新 render的次数
  • 减少重复计算
1.1、减少重新render的次数

? 组件是构成React视图的一个基本单元。有些组件会有自己本地的状态(state), 当它们的值由于用户的操作而发生改变时,组件就会重新渲染。在一个React应用中,一个组件可能会被频繁地进行渲染。这些渲染虽然有一小部分是必须的,不过大多数都是无用的,它们的存在会大大降低我们应用的性能。

  • React.memo

    在讲React.memo之前先举个🌰

    // index.js
    import React, { useState } from "react";
    import ReactDOM from "react-dom";
    import Child from  ./child 
     
    function App() {
      const [title, setTitle] = useState("这是一个 title")
     
      return (
        <div className="App">
          <h1>{ title }</h1>
          <button onClick={() => setTitle("title 改变了")}>改标题</button>
          <Child name="Child"></Child>
        </div>
      );
    }
     
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement)
    
    // child.js
    import React from "react";
     
    function Child(props) {
      console.log(props.name)
      return <h1>{props.name}</h1>
    }
     
    export default Child
    

    当我们点击Button时,虽然只改变了title,传递给子组件Child的props未发生改变,但在我们每次点击Button时,控制台都会打印一次Child。 => 子组件重新渲染了。

    当 Child 组件是一个非常大的组件,渲染一次会消耗很多的性能,那么我们就应该尽量减少这个组件的渲染,否则就容易产生性能问题,所以子组件如果在 props 没有变化的情况下,就算父组件重新渲染了,子组件也不应该渲染。

    此时我问可以通过React.memo来实现这一需求

    // child.js
    import React from "react";
     
    function Child(props) {
      console.log(props.name)
      return <h1>{props.name}</h1>
    }
     
    export default React.memo(Child)
    

    此时,当传递给Child的props不变时,Child组件便不会重新渲染。

    注:默认情况下其只会对 props 的复杂对象做浅层对比(浅层对比就是只会对比前后两次 props 对象引用是否相同,不会对比对象里面的内容是否相同),如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

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

    同样,先举个🌰

    // index.js
    import React, { useState } from "react";
    import ReactDOM from "react-dom";
    import Child from "./child";
     
    function App() {
      const [title, setTitle] = useState("这是一个 title");
      const [subtitle, setSubtitle] = useState("这是一个副title");
     
      const callback = () => {
        setTitle("标题改变了");
      };
      return (
        <div className="App">
          <h1>{title}</h1>
          <h2>{subtitle}</h2>
          <button onClick={() => setSubtitle("副标题改变了")}>改副标题</button>
          <Child onClick={callback} name="Child" />
        </div>
      );
    }
     
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    import React from "react";
     
    function Child(props) {
      console.log(props);
      return (
        <>
          <button onClick={props.onClick}>改标题</button>
          <h1>{props.name}</h1>
        </>
      );
    }
     
    export default React.memo(Child);
    

    当点击"改副标题"按钮后,副标题改变为"副标题改变了",且控制台会输出"Child",说明子组件重新渲染。

    使用了React.memo,为什么还会重新渲染?

    在函数式组件里每次重新渲染,函数组件都会重头开始重新执行,那么这两次创建的 callback 函数肯定发生了改变,所以导致了子组件重新渲染。=> 在函数没有改变的时候,重新渲染的时候保持两个函数的引用一致

    useCallback使用方法

    const callback = () => {
      doSomething(a, b);
    }
    const memoizedCallback = useCallback(callback, [a, b])
    

    把函数以及依赖项作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,这个 memoizedCallback 只有在依赖项有变化的时候才会更新, 和useEffect类似。

    // index.js
    import React, { useState, useCallback } from "react";
    import ReactDOM from "react-dom";
    import Child from "./child";
     
    function App() {
      const [title, setTitle] = useState("这是一个 title");
      const [subtitle, setSubtitle] = useState("这是一个副标题");
     
      const callback = () => {
        setTitle("标题改变了");
      };
     
      // 通过 useCallback 进行记忆 callback,并将记忆的 callback 传递给 Child
      const memoizedCallback = useCallback(callback, [])
     
      return (
        <div className="App">
          <h1>{title}</h1>
          <h2>{subtitle}</h2>
          <button onClick={() => setSubtitle("副标题改变了")}>改副标题</button>
          <Child onClick={memoizedCallback} name="Child" />
        </div>
      );
    }
     
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
  • unstable_batchedUpdates批量处理

    用于手动批量更新state,可以指定多个setState合并为一个更新请求

    如果这些setState写在React的事件处理程序(event handler)里,比如onClick,那么他们会被同时执行;

    如果这些setState写在promises,setTimeout或者非React的js原生的事件处理程序(比如document.addEventListener)里,那么他们不会被同时执行。但是可以用ReactDOM.unstable_batchedUpdates来强制同时执行。

    import React, {useState} from "react";
    import { unstable_batchedUpdates } from "react-dom";
    
    const Unstable: React.FC = () => {
      const [titleOne, setTitleOne] = useState<string>('titleOne');
      const [titleTwo, setTitleTwo] = useState<string>("titleTwo");
      const [titleThree, setTitlethree] = useState<string>("titleThree");
    
    //同时执行
    //   const onClick = () => {
    //     setTitleOne('titleOne变了');
    //     setTitleTwo('titleTwo变了');
    //     setTitlethree('titleThree变了');
    //   }
      
    //一个  
    //   const onClick = () => {
    //     setTimeout(() => {
    //       setTitleOne('titleOne变了');
    //       setTitleTwo('titleTwo变了');
    //       setTitlethree('titleThree变了');
    //     }, 0);
    //   }
    
      const onClick = () => {
        setTimeout(() => {
          unstable_batchedUpdates(() => {
            setTitleOne('titleOne变了');
            setTitleTwo('titleTwo变了');
            setTitlethree('titleThree变了');
          })
        }, 0);
      }
      
      console.log("render");
      return (
        <>
          <h1>{titleOne}</h1>
          <h3>{titleTwo}</h3>
          <h3>{titleThree}</h3>
          <button onClick={onClick}>点击</button>
        </>
      )
    }
    
    export default Unstable;
    

此时,点击按钮后只会打印一次"render".

  • 合理的拆分组件

    将一个很大组件进行适当的拆分,从而减少不必要的更新。

1.2、减少重复计算
  • useMemo

    useMemo 的使用场景主要是用来缓存计算量比较大的函数结果,可以避免不必要的重复计算

import React, { useMemo, useState } from 'react';

const UseMemo: React.FC = () => {
  const [num, setNum] = useState(0);

  const expensiveFn = () => {
    let result = 0;
    for (let i = 0; i < 10000; i++) {
    result += i;
    }
    console.log(result)
    return result;
  }

  const base = useMemo(expensiveFn, []);

  return (
    <div className="App">
    <h1>count:{num}</h1>
    <button onClick={() => setNum(num + base)}>+1</button>
    </div>
  );
}

export default UseMemo;
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:43:36  更:2022-09-30 00:44:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 17:03:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码