乐趣区

关于react.js:技术干货|7个-React-性能提升技巧


前言
React 是由 Facebook 团队主导开发的 UI 框架,作为以后三大前端框架(另外两个是 Angular、Vue) 之一,提供了数据驱动的 UI 开发模式,以及组件化的开发方式,能够构建高性能的前端利用。
拍乐云音视频 PaaS 云平台为用户提供了 Console 控制台,控制台提供了利用治理、用量查看、费用治理、数据罗盘、用户治理等性能。控制台前端的开发抉择了 React 作为根底框架。在应用 React 过程中,咱们总结了 React 性能晋升的 7 个技巧,在这里和你分享。

熟记 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. 组件拆分一个重要的准则是高内聚低耦合,子组件应该是一个独立的单元,子组件与父亲组件间的通信信息要少,数据申请应该放到子组件中,除非父组件也要应用。

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

}
2. 会复用的单元肯定要抽成组件,复用组件应该不依赖内部,因而只通过 props 进行通信,如果要应用到 context 能够用一个高阶组件进行包裹。
3. 组件粒度也不应过细,会减少组件层级深度,也会减少保护的工作量。
4. 为了对立款式,应该优先思考抽取专用的类名,而不是组件。

合并 setState

合并 setState 能够缩小 render 执行次数,是一种很间接、也很显著的性能晋升形式。
同一代码块中不要屡次调用 setState

// 调用了屡次 setState
this.setState(state => {

pagination: {
    ...state.pagination,
    current: 1,
}

}, () => {

this.setState({loading: true,});
getData().then(res => {// ...})

});

// 合并 setState
this.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 代码有帮忙。

退出移动版