[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>
    );
  }
}
相关推荐
百万蹄蹄向前冲2 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙2 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan3 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
狗狗狗狗狗乐啊3 小时前
搭一个 AI 对话工作台 AChat:从 0 到可用的完整记录(一)
python·react.js·ai编程
kyriewen4 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理4 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒5 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche15005 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情6735 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具
SoonITer5 小时前
怎样构建一个 Agent-friendly 的网站
前端·agent