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

React 组件性能最佳优化实践分享

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

React 组件性能最佳优化实践分享

React 组件性能优化最佳实践

React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virtual DOM 比对的频率。如果子组件未发生数据改变不渲染子组件。

组件卸载前进行清理操作

以下代码在组件挂载时会创建一个interval组件销毁后清除定时器,间隔1秒会触发渲染count+1,组件销毁后如果不清除定时器它会一直消耗资源

import React, { useState, useEffect } from "react"
import ReactDOM from "react-dom"

const App = () => {
  let [index, setIndex] = useState(0)
  useEffect(() => {
    let timer = setInterval(() => {
      setIndex(prev => prev + 1)
      console.log('timer is running...')
    }, 1000)
    return () => clearInterval(timer)
  }, [])
  return (
    <button onClick={() => ReactDOM.unmountComponentAtNode(document.getElementById("root"))}>
      {index}
    </button>
  )
}

export default App

每次数据更新都会触发组件重新渲染,这里的优化为:组件销毁清理定时器

类组件使用纯组件PureComponent

什么是纯组件

纯组件会对组件输入数据进行浅层比较,如果当前输入数据和上次输入数据相同,组件不会重新渲染

什么是浅层比较

比较引用数据类型在内存中的引用地址是否相同,比较基本数据类型的值是否相同。

为什么不直接进行 diff 操作, 而是要先进行浅层比较,浅层比较难道没有性能消耗吗

和进行 diff 比较操作相比,浅层比较将消耗更少的性能。diff 操作会重新遍历整颗 virtualDOM 树, 而浅层比较只操作当前组件的 state 和 props。

import React from "react"
export default class App extends React.Component {
  constructor() {
    super()
    this.state = {name: "张三"}
  }
  updateName() {
    setInterval(() => this.setState({name: "张三"}), 1000)
  }
  componentDidMount() {
    this.updateName()
  }
  render() {
    return (
      <div>
        <RegularComponent name={this.state.name} />
        <PureChildComponent name={this.state.name} />
      </div>
    )
  }
}
class RegularComponent extends React.Component {
  render() {
    console.log("RegularComponent")
    return <div>{this.props.name}</div>
  }
}

class PureChildComponent extends React.PureComponent {
  render() {
    console.log("PureChildComponent")
    return <div>{this.props.name}</div>
  }
}

组件挂载以后会有一个定时器间隔1秒设置一次name,我们可以看到RegularComponent一直在渲染,即使数据没有发生变化也会渲染。PureChildComponent只有一次渲染,因此使用纯组件会对props state进行进行比较,数据相同不会重新渲染。

shouldComponentUpdate

纯组件只能进行浅层比较,要进行深层比较,使用 shouldComponentUpdate,它用于编写自定义比较逻辑。

返回 true 重新渲染组件,返回 false 阻止重新渲染。

函数的第一个参数为 nextProps, 第二个参数为 nextState。

import React from "react"

export default class App extends React.Component {
  constructor() {
    super()
    this.state = {name: "张三", age: 20, job: "waiter"}
  }
  componentDidMount() {
    setTimeout(() => this.setState({ job: "chef" }), 1000)
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.name !== nextState.name || this.state.age !== nextState.age) {
      return true
    }
    return false
  }
  render() {
    console.log("rendering")
    let { name, age } = this.state
    return <div>{name} {age}</div>
  }
}

即使继承了Component的组件定时器一直修改数据也不会触发重新渲染

纯函数组件使用React.memo优化性能

memo 基本使用

将函数组件变为纯组件,将当前 props 和上一次的 props 进行浅层比较,如果相同就阻止组件重新渲染。

import React, { memo, useEffect, useState } from "react"
function ShowName({ name }) {
  console.log("showName render...")
  return <div>{name}</div>
}

const ShowNameMemo = memo(ShowName)
function App() {
  const [index, setIndex] = useState(0)
  const [name] = useState("张三")
  useEffect(() => {
    setInterval(() => {
      setIndex(prev => prev + 1)
    }, 1000)
  }, [])
  return (
    <div>
      {index}
      <ShowNameMemo name={name} />
    </div>
  )
}
export default App

memo 传递比较逻辑

(使用 memo方法自定义比较逻辑,用于执行深层比较。)

import React, { memo, useEffect, useState } from "react";

function ShowName({ person }) {
  console.log("showName render...");
  return (
    <div>
      {person.name}
      丨
      {person.job}
    </div>
  );
}

function comparePerson(prevProps, nextProps) {
  if (
    prevProps.person.name !== nextProps.person.name ||
    prevProps.person.age !== nextProps.person.age
  ) {
    return false
  }
  return true
}
const ShowNameMemo = memo(ShowName, comparePerson);
function App() {
  const [person, setPerson] = useState({ name: "张三", job: "developer" });
  useEffect(() => {
    setInterval(() => {
      setPerson((data) => ({ ...data, name: "haoxuan" }));
    }, 1000);
  }, []);
  return (
    <div>
      <ShowNameMemo person={person} />
    </div>
  );
}
export default App;

使用组件懒加载

使用组件懒加载可以减少 bundle 文件大小, 加快组件呈递速度。

路由组件懒加载

import React, { lazy, Suspense } from "react"
import { BrowserRouter, Link, Route, Switch } from "react-router-dom"

const Home = lazy(() => import( "./Home"))
const List = lazy(() => import( "./List"))

function App() {
  return (
    <BrowserRouter>
      <Link to="/">Home</Link>
      <Link to="/list">List</Link>
      <Switch>
        <Suspense fallback={<div>Loading</div>}>
          <Route path="/" component={Home} exact />
          <Route path="/list" component={List} />
        </Suspense>
      </Switch>
    </BrowserRouter>
  )
}
export default App

根据条件进行组件懒加载(适用于组件不会随条件频繁切换)

import React, { lazy, Suspense } from "react"

function App() {
  let LazyComponent = null
  if (true) {
    LazyComponent = lazy(() => import( "./Home"))
  } else {
    LazyComponent = lazy(() => import( "./List"))
  }
  return (
    <Suspense fallback={<div>Loading</div>}>
      <LazyComponent />
    </Suspense>
  )
}
export default App

使用Fragment 避免额外标记

为了满足这个条件我们通常都会在最外层添加一个div, 但是这样的话就会多出一个无意义的标记, 如果每个组件都多出这样的一个无意义标记的话, 浏览器渲染引擎的负担就会加剧。

import { Fragment } from "react"

function App() {
  return (
    <Fragment>
      <div>message a</div>
      <div>message b</div>
    </Fragment>
  )
}
function App() {
  return (
    <>
      <div>message a</div>
      <div>message b</div>
    </>
  )
}

不要使用内联函数定义

在使用内联函数后, render 方法每次运行时都会创建该函数的新实例, 导致 React 在进行 Virtual DOM 比对时, 新旧函数比对不相等,导致 React 总是为元素绑定新的函数实例, 而旧的函数实例又要交给垃圾回收器处理。

错误示范:

import React from "react"

export default class App extends React.Component {
  constructor() {
    super()
    this.state = {
      inputValue: ""
    }
  }
  render() {
    return (
      <input
        value={this.state.inputValue}
        onChange={e => this.setState({ inputValue: e.target.value })}
        />
    )
  }
}

正确的做法是在组件中单独定义函数, 将函数绑定给事件:

import React from "react"

export default class App extends React.Component {
  constructor() {
    super()
    this.state = {
      inputValue: ""
    }
  }
  setInputValue = e => {
    this.setState({ inputValue: e.target.value })
  }
  render() {
    return (
      <input value={this.state.inputValue} onChange={this.setInputValue} />
    )
  }
}

在构造函数中进行函数this绑定

在类组件中如果使用 fn() {} 这种方式定义函数, 函数 this 默认指向 undefined. 也就是说函数内部的 this 指向需要被更正.

可以在构造函数中对函数的 this 进行更正, 也可以在行内进行更正, 两者看起来没有太大区别, 但是对性能的影响是不同的

export default class App extends React.Component {
   constructor() {
    super()
     // 方式一
     // 构造函数只执行一次, 所以函数 this 指向更正的代码也只执行一次.
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    console.log(this)
  }
  render() {
    // 方式二 
    // 问题: render 方法每次执行时都会调用 bind 方法生成新的函数实例.
    return <button onClick={this.handleClick.bind(this)}>按钮</button>
  }
}

类组件中的箭头函数

在类组件中使用箭头函数不会存在 this 指向问题, 因为箭头函数本身并不绑定 this。

export default class App extends React.Component {
  handleClick = () => console.log(this)
  render() {
    return <button onClick={this.handleClick}>按钮</button>
  }
}

箭头函数在 this 指向问题上占据优势, 但是同时也有不利的一面.

当使用箭头函数时, 该函数被添加为类的实例对象属性, 而不是原型对象属性. 如果组件被多次重用, 每个组件实例对象中都将会有一个相同的函数实例, 降低了函数实例的可重用性造成了资源浪费.

综上所述, 更正函数内部 this 指向的最佳做法仍是在构造函数中使用 bind 方法进行绑定

优化条件渲染

频繁的挂载和卸载组件是一项耗性能的操作, 为了确保应用程序的性能, 应该减少组件挂载和卸载的次数.

在 React 中我们经常会根据条件渲染不同的组件. 条件渲染是一项必做的优化操作。

function App() {
  if (true) {
    return (
      <>
        <AdminHeader />
        <Header />
        <Content />
      </>
    )
  } else {
    return (
      <>
        <Header />
        <Content />
      </>
    )
  }
}

在上面的代码中, 当渲染条件发生变化时, React 内部在做 Virtual DOM 比对时发现, 刚刚第一个组件是 AdminHeader, 现在第一个组件是 Header, 刚刚第二个组件是 Header, 现在第二个组件是 Content, 组件发生了变化, React 就会卸载 AdminHeader、Header、Content, 重新挂载 Header 和 Content, 这种挂载和卸载就是没有必要的。

function App() {
  return (
    <>
      {true && <AdminHeader />}
      <Header />
      <Content />
    </>
  )
}

避免使用内联样式属性

当使用内联 style 为元素添加样式时, 内联 style 会被编译为 JavaScript 代码, 通过 JavaScript 代码将样式规则映射到元素的身上, 浏览器就会花费更多的时间执行脚本和渲染 UI, 从而增加了组件的渲染时间。

function App() {
  return <div style={{ backgroundColor: "skyblue" }}>App works</div>
}

避免重复无限渲染

当应用程序状态发生更改时, React 会调用 render 方法, 如果在 render 方法中继续更改应用程序状态, 就会发生 render 方法递归调用导致应用报错.

export default class App extends React.Component {
  constructor() {
    super()
    this.state = {name: "张三"}
  }
  render() {
    this.setState({name: "李四"})
    return <div>{this.state.name}</div>
  }
}

与其他生命周期函数不同, render 方法应该被作为纯函数. 这意味着, 在 render 方法中不要做以下事情, 比如不要调用 setState 方法, 不要使用其他手段查询更改原生 DOM 元素, 以及其他更改应用程序的任何操作. render 方法的执行要根据状态的改变, 这样可以保持组件的行为和渲染方式一致.

避免数据结构突变

组件中 props 和 state 的数据结构应该保持一致, 数据结构突变会导致输出不一致.

import React, { Component } from "react"

export default class App extends Component {
  constructor() {
    super()
    this.state = {
      employee: {
        name: "张三",
        age: 20
      }
    }
  }
  render() {
    const { name, age } = this.state.employee
    return (
      <div>
        {name}
        {age}
        <button
          onClick={() =>
            this.setState({
              ...this.state,
              employee: {
                ...this.state.employee,
                age: 30
              }
            })
          }
        >
          change age
        </button>
      </div>
    )
  }
}

到此这篇关于React 组件性能最佳优化实践分享的文章就介绍到这了,更多相关React 组件性能优化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

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

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

React 组件性能最佳优化实践分享

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

下载Word文档

猜你喜欢

PHP 性能优化:社区分享与最佳实践

通过结合社区经验和最佳实践,可有效优化 php 应用性能。社区分享的技巧包括使用缓存、优化数据库查询和使用 cdn。最佳实践包括启用 opcache、使用队列、避免代码重复和进行性能监控。实践案例表明,通过使用缓存机制缓存常见数据库查询结果
PHP 性能优化:社区分享与最佳实践
2024-05-11

【性能分析】VUE 计算属性性能优化最佳实践

本文探究了 Vue 计算属性的性能优化最佳实践,深入剖析了如何提升计算属性的执行效率,避免性能瓶颈,打造流畅响应的 Vue 应用程序。
【性能分析】VUE 计算属性性能优化最佳实践
2024-02-20

C++性能优化的最佳实践有哪些?

为了优化 c++++ 性能,可以使用这些最佳实践:使用内联函数来提高小型函数的效率。避免不必要的拷贝,使用引用或指针代替。使用缓存来减少访问外部内存的开销。优化容器以提高查找性能,例如使用 unordered_map。避免使用虚拟函数以减少
C++性能优化的最佳实践有哪些?
2024-05-08

JavaScript 渲染性能优化最佳实践:让你的网站性能更佳

JavaScript、渲染性能、优化、网站性能
JavaScript 渲染性能优化最佳实践:让你的网站性能更佳
2024-02-09

PHP Redis扩展最佳实践:优化性能与可靠性

PHP Redis扩展通过提供对Redis服务器的访问来帮助开发人员轻松地使用Redis,本文将介绍如何优化PHP Redis扩展的性能和可靠性,以确保应用程序的最佳运行状态。
PHP Redis扩展最佳实践:优化性能与可靠性
2024-02-07

Java RESTful API 的最佳实践:优化性能和安全性

构建高性能、安全的 Java RESTful API:最佳实践
Java RESTful API 的最佳实践:优化性能和安全性
2024-03-07

Golang 函数的性能优化技巧和最佳实践

优化 go 函数性能的技巧包括:基准测试代码以确定优化前的性能指标。避免不必要的内存分配,使用内存池或对象池。使用切片而不是数组。避免不必要的函数调用,考虑内联小函数或使用闭包。避免不必要的同步,使用轻量级同步机制。优化代码结构,将相关代码
Golang 函数的性能优化技巧和最佳实践
2024-05-23

PHP 数组键和值颠倒:性能优化策略与最佳实践

php 中数组键值颠倒的性能优化策略包括:1. 使用 array_flip() 函数;2. 尽量避免数组副本。实例如将商品名称和价格键值颠倒,使用 array_flip() 函数可快速高效地完成此操作。PHP 数组键值颠倒:性能优化策略与实
PHP 数组键和值颠倒:性能优化策略与最佳实践
2024-04-30

Golang函数并发编程:性能优化最佳实践

在 go 函数中实现并发编程性能优化,最佳实践包括:限制协程数量以避免资源争用;使用管道进行轻量级通信,避免数据竞争;并行处理任务,而非顺序执行;实战案例:使用并发爬虫高效处理数据。Golang 函数并发编程:性能优化最佳实践在 Go 语
Golang函数并发编程:性能优化最佳实践
2024-04-17

PHP 函数调用性能优化实践分享

为提升 php 应用性能,优化函数调用至关重要。实践包括:减少不必要的函数调用(如重复调用、传入不必要参数)利用函数别名和缩写使用内联函数(提升简单函数调用的性能)PHP 函数调用性能优化实践分享在 PHP 开发中,函数调用会消耗大量时间
PHP 函数调用性能优化实践分享
2024-04-17

优化Golang函数参数传递性能的最佳实践

为了优化 go 函数参数传递性能,最佳实践包括:使用值类型避免复制小型值类型;使用指针传递大型值类型(结构体);使用值类型传递切片;使用接口传递多态类型。在实践中,传递大型 json 字符串时,传递 data 参数指针可以显著提高反序列化性
优化Golang函数参数传递性能的最佳实践
2024-04-13

PHP实现抽奖功能的最佳实践分享

PHP实现抽奖功能的最佳实践分享随着互联网的发展,抽奖活动在各种网站和应用中变得越来越普遍,作为一种促销和互动手段,抽奖活动能够有效吸引用户,提升用户参与度和忠诚度。在网站或应用中如何实现一个简单而实用的抽奖功能呢?本文将分享PHP实现抽
PHP实现抽奖功能的最佳实践分享
2024-03-01

探讨网站性能优化设计的最佳实践和案例分析

网站性能优化设计的最佳实践与案例分析随着网络技术的迅猛发展,越来越多的企业和个人都拥有了自己的网站。然而,随之而来的是网页加载速度变慢、响应时间变长等问题,给用户的体验产生了负面影响。因此,对于网站性能的优化设计成为了刻不容缓的任务。网
探讨网站性能优化设计的最佳实践和案例分析
2024-02-02

服务器性能优化最佳实践:从零基础到性能大师

服务器性能优化最佳实践:从新手到大师
服务器性能优化最佳实践:从零基础到性能大师
2024-02-15

编程热搜

目录