React组件性能优化实践

这篇具有很好参考价值的文章主要介绍了React组件性能优化实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

React组件性能优化最佳实践

React组件性能优化的核心是减少渲染真实DOM节点的频率,减少 Virtual DOM比对的频率。

组件卸载前进行清理操作

在组件中为 window注册的全局事件,以及定时器,在组件卸载前要清理掉,防止组件卸载后继续执行影响应用性能。

需求:开启定时器,然后卸载组件,查看组件中的定时器是否还在运行。

function Test(){
    useEffect(()=>{
        let timer = setInterval(()=>{
            console.log("定时器在执行");
        },1000);
        return ()=>clearInterval(timer);
    },[]);
}

通过纯组件提升组件性能(类组件)

  1. 什么是纯组件?

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

  1. 什么是浅层比较

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

  1. 如何实现纯组件

类组件继承 PureComponent 类,函数组件使用 memo 方法

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

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

  1. 需求:在状态对象中存储 name 值为张三,组件挂载完成后将 name 属性的值再次更改为张三,然后分别将 name 传递给纯组件和非纯组件,查看结果。
class App extends Component{
    constructor(){
        super();
        this.state = {
            name: "张三"
        }
    }
    updateName(){
        setInterval(()=>{
            this.setState({name:"张三"});
        },1000);
    }
    componentDidMount(){
        this.updateName();
    }
    render(){
        return <>
            <ReguarComponent name={this.state.name} />
            <PureComponentDemo name={this.state.name} />
        </>
    }
}

class ReguarComponent extends Component{
    render(){
        console.log('ReguarComponent');
        return <div>{this.props.name}</div>
    }
}
class PureComponentDemo extends PureComponent{
    render(){
        console.log('PureComponentDemo');
        return <div>{this.props.name}</div>
    }
}

通过 shouldComponentUpdate 生命周期函数提升组件性能

纯组件只能进行浅层比较,要进行深层比较,使用 shouldComponentUpdate,它用于编写自定义比较逻辑。返回 true 重新渲染组件,返回 false 阻止重新渲染。

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

需求:在页面中展示员工信息,员工信息包括:姓名、年龄、职位,但是在页面中只想展示姓名和年龄,也就是说只有姓名和年龄发生变化时才有必要重新渲染组件,如果员工的其他信息发生了变化没必要重新渲染组件。

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

通过纯组件提升组件性能(函数组件)

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

需求:父组件维护两个状态,index 和 name,开启定时器让 index 不断发生变化,name 传递给子组件,查看父组件更新子组件是否也更新了。

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

使用 memo方法自定义比较逻辑,用于执行深层比较。比较函数的第一个参数为上一次的 props,比较函数的第二个参数为下一次的 props,比较函数返回 true,不进行渲染,比较函数返回 false,组件重新渲染。

function comparePerson(prevProps,nextProps){
    if(prevProps.person.name!==nextProps.person.name||prevProps.person.age!==nextProps.person.age){
        return false;
    }
    return true;
}
const ShowPersonMemo = memo(ShowPerson,comparePerson);

使用组件懒加载

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

  1. 路由组件懒加载
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>)
}
  1. 根据条件进行组件懒加载,适用于组件不会随条件频繁切换
function App(){
    let LazyComponent = null;
    if(true){
        LazyComponent = lazy(()=>import("./Home"));
    }else{
        LazyComponent = lazy(()=>import("./List"));
    }
    return (<Suspense fallback={<div>Loading</div>}>
        <LazyComponent />
    </Suspense>);
}

使用占位符标记提升 React 组件的性能

React 组件中返回的 jsx 如果有多个同级元素,多个同级元素必须要有一个共同的父级。

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

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

为了解决这个问题,React 推出了 fragment 占位符标记,使用占位符标记既满足了拥有共同父级的要求又不会多出额外的无意义标记。

import {Fragment} from "react"

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

如果你觉得 Fragment 比较烦琐也可以写空占位符。

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

避免使用内联函数

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

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})}/>)
    }
}

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

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 进行更正。也可以在行内进行更正。两者看起来没有太大区别,但是对性能的影响是不同的。

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。

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

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

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

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

避免使用内联样式属性

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

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

在上面的组件中,为元素附加了内联样式,添加的内联样式为 JavaScript 对象,backgroundColor 需要被转换为等效的 CSS 样式规则,然后将其应用到元素,这样涉及到脚本的执行。

更好的办法是将 CSS 文件导入样式组件,能通过 CSS 直接做的事情就不要通过 JavaScript 去做,因为 JavaScript 操作 DOM 非常慢。

优化条件渲染

频繁的挂载和卸载组件是一项耗性能的操作,为了确保应用程序的性能,应该减少组件挂载和卸载的次数。在 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 />
    </>)
}

避免重复的无限渲染

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

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

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

为组件创建错误边界

默认情况下,组件渲染错误会导致整个应用程序中断,创建错误边界可确保在特定组件发生错误时应用程序不会中断。

错误边界是一个 React 组件,可以捕获子级组件在渲染时发生的错误,当错误发生时,可以将错误记录下来,可以显示备用 UI 界面。错误边界涉及到两个生命周期函数,分别是 getDerivedStateFromError 和 componentDidCatch。getDerivedStateFromError为静态方法,方法中需要返回一个对象,该对象会和 state 对象进行合并,用于更改应用程序状态。componentDidCatch方法用于记录应用程序错误信息,该方法的参数就是错误对象。

class ErrorBoundaries extends React.Component{
    constructor(){
        super()
        this.state={
            hasError:false
        }
    }
    componentDidCatch(error){
        console.log("componentDidCatch")
    }
    static getDerivedStateFromError(){
        console.log("getDerivedStateFromError");
        return {
            hasError: true
        }
    }
    render(){
        if(this.state.hasError){
            return <div>发生了错误</div>
        }
        return <App />
    }
}
ReactDom.render(<ErrorBoundaries />,document.getElementById("root"))

注意:错误边界不能捕获异步错误,比如点击按钮时发生的错误。

避免数据结构突变

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

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:{
                    age:30
                }
            })}>change age</button>
        </div>)
    }
}

优化依赖项大小

在应用程序中经常会依赖第三方包,但我们不想引用包中的所有代码,我们只想用到哪些代码就包含哪些代码。此时可以使用插件对依赖项进行优化。文章来源地址https://www.toymoban.com/news/detail-634289.html

到了这里,关于React组件性能优化实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于 React 性能优化和数栈产品中的实践

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:的卢 在日常开发过程中,我们会使用很多性能优化的 API ,比如像使用 memo 、 useMemo 优化组件或者值,再比如使用 shouldCo

    2024年02月08日
    浏览(28)
  • 性能优化-react路由懒加载和组件懒加载

    随着项目越来越大,打包后的包体积也越来越大,严重影响了首屏加载速度,需要对路由和组件做懒加载处理 主要用到了react中的lazy和Suspense。 废话不多说,直接上干货 核心代码 配合路由表的完整例子 效果 组件加载前 组件懒加载后 这样就会大大加快首屏加载速度

    2024年02月14日
    浏览(26)
  • 【three.js / React-three-fiber】加载3D模型性能优化

    无论是大型虚拟世界还是简单的网站,性能优化都是必要的。 特别是在运用三维模型的情况下,我们需要更加深入的优化。因为 三维模型通常包含大量的数据和复杂的几何形状 ,如果不进行性能优化,浏览器可能会因为负载过重而崩溃。 在本文中,我们将探讨如何在 thre

    2024年02月02日
    浏览(36)
  • 【前端知识】React 基础巩固(三十四)——组件中的异步操作及优化

    通过组件的生命周期来完成网络请求,网络请求的异步代码直接放在组件中 通过redux来管理异步网络请求 在store中引入中间件 redux-thunk 构建 fetchHomeMultidataAction ,将原本在组件中的异步请求代码放入到actionCreators.js中 改写原来的category.jsx,派发异步请求的dispatch 查看运行结果

    2024年02月15日
    浏览(52)
  • 理解React页面渲染原理,如何优化React性能?

    当使用React编写应用程序时,可以使用JSX语法来描述用户界面的结构。JSX是一种类似于HTML的语法,但实际上它是一种JavaScript的扩展,用于定义React元素。React元素描述了我们想要在界面上看到的内容和结构。 在运行React应用程序时,JSX会被转换成真实的DOM元素,这个过程主要

    2024年02月08日
    浏览(28)
  • React Native性能优化指南

    本文将介绍在React Native开发中常见的性能优化问题和解决方案,包括ScrollView内无法滑动、热更新导致的文件引用问题、高度获取、强制横屏UI适配、低版本RN适配iOS14、缓存清理、navigation参数取值等。通过代码案例演示和详细说明,帮助开发者更好地理解和解决React Native中的

    2024年01月23日
    浏览(36)
  • 5.React.memo 性能优化

    性能优化, React.memo 2. React.memo类似纯组件,可提高组件性能表现(类组件PureComponent)

    2024年02月11日
    浏览(26)
  • react组件编写最佳实践

    作为 React.js 开发的新手,理解基本原则并坚持实用性对于更好地快速开发至关重要。无论我们是在处理简单的任务(例如构建初学者待办事项应用程序)还是更复杂的项目(例如建立电子商务平台),遵循基本要素都将帮助我们编写出可根据业务需求轻松维护的代码。 凭借

    2024年02月14日
    浏览(28)
  • React Hooks ——性能优化Hooks

    Hooks从语法上来说是一些函数。这些函数可以用于在函数组件中引入状态管理和生命周期方法。 简洁 从语法上来说,写的代码少了 上手非常简单 基于函数式编程理念,只需要掌握一些JavaScript基础知识 与生命周期相关的知识不用学,react Hooks使用全新的理念来管理组件的运作

    2024年02月06日
    浏览(33)
  • React性能优化之memo缓存函数

    React是一个非常流行的前端框架,但是在处理大型应用程序时,性能可能会成为一个问题。为了解决这个问题,React提供了一个称为memo的功能,它可以缓存函数并避免不必要的重新渲染。 memo是React中的一个高阶组件(HOC),它接收一个组件并返回一个新的组件。这个新组件具

    2024年02月11日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包