[React] 性能优化相关 (二)

文章目录

1.避免使用内联对象

使用内联对象时,react会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象。因此,该组件对于props的千层比较始终返回false,导致组件一直渲染。

js 复制代码
// Don't do this!
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AComponent style={{ margin: 0 }} aProp={aProp} />  
}

// Do this instead :)
const styles = { margin: 0 };
function Component(props) {
  const aProp = { someProp: 'someValue' }
  return <AComponent style={styles} {...aProp} />  
}

2.避免使用匿名函数

虽然匿名函数是传递函数的好方法,但它们在每次渲染上都有不同的引用。类似于内联对象。

为了保证作为props传递给react组件的函数的相同引用,如果使用的类组件可以将其声明为类方法,如果使用的函数组件,可以使用useCallback钩子来保持相同的引用。

js 复制代码
// 避免这样做
function Component(props) {
  return <AComponent onChange={() => props.callback(props.id)} />  
}

// 函数组件,优化方法一
function Component(props) {
  const handleChange = useCallback(() => props.callback(props.id), [props.id]);
  return <AComponent onChange={handleChange} />  
}

// 类组件,优化方法二
class Component extends React.Component {
  handleChange = () => {
   this.props.callback(this.props.id) 
  }
  render() {
    return <AComponent onChange={this.handleChange} />
  }
}

3.延迟加载不是立即需要的组件

React.lazy和React.Suspense完成延迟加载不是立即需要的组件。React加载的组件越少,加载组件的速度越快。

js 复制代码
// React.lazy 接受一个函数,这个函数需要动态调用 import()引入组件
const HomeIndex = React.lazy(() => import('@/modules/home'))
......

// 然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)
return(
    <React.Suspense fallback={<>Loading...</>}>
        <HomeIndex />
    </React.Suspense>
)


// 一般会封装一个公共的方法
const withLoadingComponent = (comp: JSX.Element) => (
  <React.Suspense fallback={<>Loading...</>}>
    {comp}
  </React.Suspense>
)

// 调用方法,传入要延迟加载的组件
return(
	{withLoadingComponent(<HomeIndex />}
)

4.调整CSS而不是强制组件加载和卸载

有时保持组件加载的同时,通过CSS隐藏可能是有益的,而不是通过卸载来隐藏。对于具有显著的加载或卸载时序的重型组件而言,这是有效的性能优化手段。

将元素透明度调整为0对浏览器的成本消耗几乎为0(因为它不会导致重排),并且应该尽可能优先更改visibility或display。

js 复制代码
// 避免对大型的组件频繁对加载和卸载
function Component(props) {
  const [view, setView] = useState('view1');
  return view === 'view1' ? <AComponent /> : <BComponent />  
}

// 使用该方式提升性能和速度
const visibleStyles = { opacity: 1 };
const hiddenStyles = { opacity: 0 };
function Component(props) {
  const [view, setView] = useState('view1');
  return (
    <React.Fragment>
      <AComponent style={view === 'view1' ? visibleStyles : hiddenStyles}>
      <BComponent style={view !== 'view1' ? hiddenStyles : visibleStyles}>
    </React.Fragment>
  )
}

5.使用React.Fragment避免添加额外的DOM

有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在react规定组件中必须有一个父元素。

js 复制代码
	<h1>Hello world!</h1>
	<h1>Hello there!</h1>
	<h1>Hello there again!</h1>

为了减少不必要的加载时间,我们可以使React.Fragment来避免创建不必要的元素。

js 复制代码
function Component() {
        return (
            <React.Fragment>
                <h1>Hello world!</h1>
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            </React.Fragment>
        )
}

6.使用React.PureComponent , shouldComponentUpdate

父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。

在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染。

js 复制代码
import React, { Component, PureComponent } from "react";

// 没有变化不会触发render, 有变化触发render: 
// 组件优化: 1.只要执行了setState, 都会触发render 2.触发render, 会render组件
// 只有当前state和props数据发生改变的话才触发render
// shouldComponentUpdate() 返回为 true
export default class Parent extends Component {
  state = { count: 0 };

  // 重写 shouldComponentUpdate()
  shouldComponentUpdate(nextProps, nextState){
    if(this.state.count === nextState.count) return false
    else return true
  }

  // 如果修改的对象和以前的对象有一点联系的话, 不会触发render, 比如就修改对象的名称, 不修改对象的地址
  render() {
    const { count } = this.state;
    return (
      <div className="parent">
        Parent
        <br />
        {count}
        <button onClick={() => this.setState({ count: 1 })}>1</button>
        <Chird count={count} />
      </div>
    );
  }
}

class Chird extends PureComponent {
  // shouldComponentUpdate(nextProps, nextState){
  //   return !this.props.count === nextProps.count
  // }

  render() {
    return (
      <div className="child">
        Chird
        <br />
        {this.props.count}
      </div>
    );
  }
}
相关推荐
QQ1__8115175153 小时前
Spring boot名城小区物业管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
钛态3 小时前
前端微前端架构:大项目的救命稻草还是自找麻烦?
前端·vue·react·web
一粒黑子3 小时前
【实战解析】阿里开源 PageAgent:纯前端 GUI Agent,一行JS让网页支持自然语言操控
前端·javascript·开源
独角鲸网络安全实验室3 小时前
2026微信小程序抓包全解析:从实操落地到合规风控,解锁前端调试新范式
前端·微信小程序·小程序·抓包·系统代理绕过·https证书严格校验·进程隔离
紫微AI3 小时前
前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了
前端·人工智能·typescript
GISer_Jing3 小时前
AI前端(From豆包)
前端·aigc·ai编程
IT枫斗者3 小时前
前端部署后如何判断“页面是不是最新”?一套可落地的版本检测方案(适配 Vite/Vue/React/任意 SPA)
前端·javascript·vue.js·react.js·架构·bug
测试修炼手册3 小时前
[测试技术] 深入理解 JSON Web Token (JWT)
前端·json
AI老李3 小时前
2026 年 Web 前端开发的 8 个趋势!
前端
里欧跑得慢3 小时前
15. Web可访问性最佳实践:让每个用户都能平等访问
前端·css·flutter·web