项目实战中react性能优化总结

react介绍

React 是由 Facebook 团队主导开发的UI框架,作为当前三大前端框架(另外两个是 Angular、Vue )之一,提供了数据驱动的 UI 开发模式,以及组件化的开发方式,可以构建高性能的前端应用

Diffing 规则

  1. 当节点(包括 DOM 节点和组件节点)的类型发生变化时,React 会卸载该节点(包含子节点)的 DOM 树,然后重新渲染新的 DOM 树。
  2. 当 DOM 节点的类型不变时,React 会保留 DOM 节点,然后对比属性变化更新 DOM 节点
  3. 当组件节点的类型不变时,组件实例会保持不变,对比 props 属性然后更新组件。
  4. 上面过程从根节点开始,递归到所有的叶子节点结束

​ 从上面的规则我们可以看出,组件是主角,后面的性能提升点也是围绕组件的。从上面的规则可以看出大变动应该使用不同的组件, 小变动应该修改属性。记住了 Diffing 规则其实就可以写出性能不错的 React 代码。上面只是一个总结,不需要可以详细看看这遍文档协调。

key

更新时 React 需要对比更新前后的变化,生成一个 mutation。没有 key,不能复用之前的组件,影响性能。

用数组索引来作为 key,由于是基于索引来决定组件的更新和复用,顺序变化时会导致非受控组件变化达不到预期,比如期望第一个元素和第二个元素对调位置,由于数组索引作为 key,非受控组件是复用的(后面有个性能提升技巧就是使用非受控组件),React 识别不出来位置变化,这里有个例子演示了索引作为 key 导致的问题。

最佳实践是在列表层使用唯一值作为 key,例如 id。

合理地拆分组件

我们知道 React 提供了组件式开发方式。写好 React 代码的基础就是组件拆分,合理地组件拆分既可以提升的代码可维护性和可复用性,结合后面的性能技巧,还可以提升性能。

  1. 组件拆分一个重要的原则是高内聚低耦合,子组件应该是一个独立的单元,子组件与父亲组件间的通信信息要少,数据请求应该放到子组件中,除非父组件也要使用。

  2. 会复用的单元一定要抽成组件,复用组件应该不依赖外部,因此只通过 props 进行通信,如果要使用到 context 可以用一个高阶组件进行包裹

    function Page(props) => {
          
          
       return <Card> 
            <MeetingInfo id={
          
          props.match.params.id} /> 
            <UserList /> 
       </Card>;}
    
    function MeetingInfo(props) {
          
          
       const {
          
           info, setInfo } = useState({
          
          }); 
       useEffect(() => {
          
           
          getMeeting(props.id).then(res => {
          
           setInfo(res); }); 
       }); 
       return <> {
          
           // 这里显示meetingInfo } </>;
    }
    
  3. 组件粒度也不应过细,会增加组件层级深度,也会增加维护的工作量

  4. 为了统一样式,应该优先考虑抽取公用的类名,而不是组件

合并setState

合并 setState 可以减少 render 执行次数,是一种很直接、也很明显的性能提升方式。

同一代码块中不要多次调用 setState

// 调用了多次setState

this.setState(
   state => {
    
    
      pagination: {
    
     ...state.pagination, current: 1, }}, 
      () => {
    
     this.setState({
    
     loading: true, }); 
      getData().then(res => {
    
     // ... })});
      // 合并setStatethis.setState(
      	state => {
    
     
      	 pagination: {
    
     ...state.pagination, current: 1, }, 
      	 loading: true,}, 
      	 () => {
    
     
      	   getData().then(res => {
    
     // ... })
    });

同时发出的请求,尤其当这些请求耗时差不多时,可以使用 Promise.all 合并,从而减少 setState 次数

// 合并前

getMeeting().then(info => this.setState({ info }));
getUserList().then(userList => this.setState({ info }));// 合并后Promise.all([getMeeting(), getUserList(),]).then(([info, userList]) => { this.setState({ info, userList, });});

避免在 render 中做复杂的计算

理想的情况,render 只做数据的展示,实际开发中或多或少会有一些计算,但是要避免在 render 中做复杂的计算,而应该把值计算好存储在 state 或者 props 中。

// 再render中做了构建树的计算

function DeptTree(props) {
    
    return <Tree data={
    
    buildTree(props.list)} />}// 应该把树结构数据计算好存储在state中function DeptTree(props) { 
   const {
    
     treeData, setTreeData } = useState(); 
   useEffect(() => {
    
     setTreeData(buildTree(props.list)); }, [ props.list ]); 
   return <Tree data={
    
    treeData} />
}

使用 PureComponent

PureComponent 更新前会浅比较 props 和 state 是否发生变化,如果没有发生变化时则不调用 render ,从而达到提升性能的目的。

import { useState } from 'react';
import Child from './Child';
function Component() {
   const [ num, setNum ] = useState(1); 
   return <div> 
      <div> 
        <button onClick={() => setNum(num + 1)}>添加({num})</button> 
      </div> 
      <Child /> 
    </div>;
 }
 export default Component;
// child.js

import React from 'react';
class Child extends React.PureComponent {
   render() { 
      console.log('render') 
      return <div>子组件</div>; 
      }
   }
export default Child;

因为使用了 PureComponent ,每次 num 增加,子组件的 render 并不会调用。PureComponent 做的是浅比较,因此要使用不可变数据。

使用非受控组件

我们先看下什么是受控组件,就是子组件的状态由依赖父组件,这样会导致需要更新子组件时,必须先改变父组件的状态,也就是触发父组件的 render,针对这种情况应该考虑是否可以设计为非受控组件,其实形成父子组件,大部分情况肯定是要通信的,为了避免形成受控关系,可以使用 context(通常使用 Redux ,简化了 context 的使用)进行通信,常见的弹框,控制弹框显示的值父组件是不需要使用的,通过把这个放在 context 中,可以在弹框的打开和关闭不触发父组件的 render ,可以很明显的提升性能,以下是示意代码。

// addSuccess是一个固定不变的函数,因此这个组件是个非受控组件

<Add onSuccess={
    
    this.addSuccess} />

function Add(props) {
    
    
  return <Modal visible={
    
     props.addVisible }> 
    {
    
     // ... } 
    </Modal>;
 }
 export default connect(state => ({
    
     addVisible: state[namespace].addVisible,}))(Add)

总结

以上总结的性能提升技巧,合并 setState、使用 PureComponent、使用非受控组件都是通过减少 render 次数来达到性能优化的,也是主要的性能方向。当然合并 setState 为了性能优化牺牲了代码的可读性,大型项目建议采用,小型项目可以权衡取舍。最后一个使用非受控组件是为了降低父子组件之间的耦合(也就是合理地拆分组件)而总结出来的,不但提升了代码的可维护性还提升了性能。希望阅读完这篇文章能对你写出高性能的 React 代码有帮助。

*陆荣涛前端学习交流Q群858752519
加群备注:CSDN推荐

猜你喜欢

转载自blog.csdn.net/sdasadasds/article/details/125684234
今日推荐