前言
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 代码有帮忙。