在内部,React使用好几种聪明的技巧去最小化更新UI所需要的DOM操作。对于很多应用来说,使用React会使得构建用户界面非常之快而且不需要做太多专门的性能优化。虽然如此,还是有一些方法可以让你为React应用加速。

使用生产构建

如果你正在性能测试或者在你的应用里遇到性能测试问题,确保你测试时使用了压缩了的生产构建:

  • 对于创建React应用,你需要运行npm run build然后遵循指令
  • 对于单文件构建,我们提供生产环境.min.js的文件版本
  • 对于模块管理,你需要设置NPDE_ENV=production
  • 对于webpack,你需要添加这个到配置文件的plugins里
new webpack.DefinePlugin({'process.env': {NODE_ENV: JSON.stringify('production')}
}),
new webpack.optimize.UglifyJsPlugin()

  • 对于汇总,你需要在commonjs插件之前使用replace插件因此只在开发环境使用的模块就不会被导入。完整的设置例子请看这里。
plugins: [require('rollup-plugin-replace')({'process.env.NODE_ENV': JSON.stringify('production')}),require('rollup-plugin-commonjs')(),// ...
]

开发的构建包括了额外的警告很有帮助但是由于额外的统计所以会让程序变慢。

使用chrome performance对组件进行性能分析

在开发模式下,你可以通过使用浏览器里的性能工具来显示组件的实例化,更新和销毁。举个例子:

在chrome浏览器里这样做:

  1. 加载你的应用,使地址url的查询字符串为?react_perf
  2. 打开chrome开发者工具的performance面板并且按下record
  3. 然后做一些你想要测试分析的动作。不要录制超过20秒否则chrome可能会挂起
  4. 停止录制
  5. React事件将会成组地出现在user timing标签下面

注意那些数字是相对的因此组件在生产环境下会渲染地更快。还有,这样可以帮助你意识到不相关的UI会错误的更新,还有UI更新的深度和频率。

如今的chrome,edge和IE浏览器支持这个特性,但是我们使用的标准user timing API因此我们希望更多的浏览器可以添加对它的支持。

避免重复渲染

React在渲染出的UI内部建立和维护了一个内层的实现方式。这个内部表示包含了从组建里返回的React元素。这个内部表示让React避免了不必要的创建和关联DOM节点,那样会使速度变慢。有时被提到为“虚拟DOM”,但是在React Native里它同样存在。

当一个组件的props或者state改变了,React通过比较新返回的元素和之前渲染的元素来决定是否一个DOM的更新是必要的。当两者不一样的时候,React会更新DOM。

在一些情况下,你的组件通过重写生命周期函数shouldComponentUpdate可以为程序加速,shouldComponentUpdate是在重新渲染的流程开始之前被触发。这个函数默认会返回true,委托React去更新:

shouldComponentUpdate(nextProps, nextState) {return true;
}

如果你知道在某些情况下你的组件不需要更新,你可以在shouldComponentUpdate里返回false,来跳过整个渲染流程,包括对该组件和之后的内容调用render()方法。

shouldComponentUpdate应用

下面是组件的树状目录。对于每一个节点,SCU指明了shouldComponentUpdate返回了什么,而vDOMEq指明了是否已经渲染的React元素发生了变化。最终,圆圈的颜色表明了是否组件需要重新渲染。

自从shouldComponentUpdate在树的节点C2处返回了false,React不会试图渲染C2节点,因此在C4和C5节点上也不需要调用shouldComponentUpdate。

对于C1和C3节点,shouldComponentUpdate返回了true,因此React必须往下到叶子节点去检查它们。对于C6shouldComponentUpdate返回了true,自从元素已经发生改变React就必须更新DOM。

最有趣的情况是C8。React必须渲染这个组件,但是自从React元素返回的和之前渲染的一样,那就不必更新DOM。

注意React只是必须改变C6的DOM,这是不可避免的。对于C8,它通过比较跳出了更新,并且对于C2的子树和C7,甚至不需要比较因为shouldComponentUpdate返回了false,所以render就不会被调用。

例子

如果想让组件只在props.color或者state.count的值变化时重新渲染,你可以像下面这样设定shouldComponentUpdate:
class CounterButton extends React.Component {constructor(props) {super(props);this.state = {count: 1};}shouldComponentUpdate(nextProps, nextState) {if (this.props.color !== nextProps.color) {return true;}if (this.state.count !== nextState.count) {return true;}return false;}render() {return (<buttoncolor={this.props.color}onClick={() => this.setState(state => ({count: state.count + 1}))}>Count: {this.state.count}</button>
    );}
}

在这段代码里,shouldComponentUpdate检查了props.color和stete.count的值是否有变化。如果它们没有变化,那么组件就不更新。如果你的组件越复杂,你就可以对于props和state使用“表面对比”类似的模式来决定是否组件应该更新。这个模式很常见,React提供了一个帮助工具来实现这个逻辑,它继承自React.PureComponent。所以下面的代码用简单的方式实现了同样的事:

class CounterButton extends React.PureComponent {constructor(props) {super(props);this.state = {count: 1};}render() {return (<buttoncolor={this.props.color}onClick={() => this.setState(state => ({count: state.count + 1}))}>Count: {this.state.count}</button>
    );}
}

大多数情况,可以使用React.PureComponent取代你自己写的shouldComponentUpdate。它只会做一个浅比较,所以当一个props或者state以某种方式突变那么浅比较可能会错过这个变化。

这在复杂的数据结构时就会出现问题。举个例子,这么说吧你想要一个ListOfWords组件去渲染一个逗号隔开的单词表,它会有一个WordAdder父组件让你按一下按钮就在列表添加一个单词。下面的代码运行会出错:

class ListOfWords extends React.PureComponent {render() {return <div>{this.props.words.join(',')}</div>;
  }
}class WordAdder extends React.Component {constructor(props) {super(props);this.state = {words: ['marklar']};this.handleClick = this.handleClick.bind(this);}handleClick() {// This section is bad style and causes a bugconst words = this.state.words;words.push('marklar');this.setState({words: words});}render() {return (<div><button onClick={this.handleClick} /><ListOfWords words={this.state.words} /></div>
    );}
}

问题就在于PureComponent会做一个简单的比较在新的和旧的this.props.words之间。自从WordAdder类里的handleClick方法里的words数组发生了改变,旧的和新的this.props.words的值会比较为相同的,即使数组中的单词真的发生了变化。ListOfWords因此就不会更新即使它拥有了新的单词。

不会突变的数据的力量

最简单的方法去避免这个问题就是避免去使用可能会突变的props或者state。举个例子,上面的handleClick方法可以使用concat重写:

handleClick() {this.setState(prevState => ({words: prevState.words.concat(['marklar'])}));
}

ES6支持一种对于数组的扩展操作符可以让这里更简单。如果你是创建React App,那么这个语法默认是可用的。
handleClick() {this.setState(prevState => ({words: [...prevState.words, 'marklar'],}));
};

你也可以重写改变对象的代码为了避免这个突变,通过类似的方式。举个例子,我们有一个对象名字叫做colormap并且我们想写一个函数来改变colormap.right为'blue'。我们可以这样写:

function updateColorMap(colormap) {colormap.right = 'blue';
}

不需改变原来的对象,我们可以使用Object.assign方法:
function updateColorMap(colormap) {return Object.assign({}, colormap, {right: 'blue'});
}

updateColorMap现在返回一个新对象,而不是改变旧的对象。Object.assign在ES6中并且要求一个polyfill。

这里有一个js建议要添加对象扩展操作符使得不修改而更新对象更加简便:

function updateColorMap(colormap) {return {...colormap, right: 'blue'};
}

如果你正在创建React App,Object.assign和扩展操作符语法都默认是可用的。

使用不可变的数据结构

Immutable.js是解决这个问题的另一种方法。它通过结构共享提供不可突变的,持久的集合:
  • 不可变的:一旦创建,一个合集在其他时间点不能被改变。
  • 执着的:新的合集可以通过前一个合集和一个改变来建立。原始的集合在新的集合建立后依然可用。
  • 结构分享:新的合集尽可能多的使用和原始集合同样的结构来创建,减少复制到最低限度来提高性能。

不可变性使得追踪改变很简单。每个变化都会导致产生一个新的对象,因此我们只需检查索引对象是否改变。举个例子,在这段js代码中:

const x = { foo: "bar" };
const y = x;
y.foo = "baz";
x === y; // true

虽然y被编辑了,自从它和x引用的是同一个对象,这个比较返回了true。你可以使用immutable.js来写相似的代码:
const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar'  });
const y = x.set('foo', 'baz');
x === y; // false

在这个例子中,自从改变了x一个新的引用返回,我们可以设想x被改变了。

另外两个可以帮助我们使用不可改变的数据的库是seamless-immutable和immutability-helper。

不可变的数据结构提供了方便的方式来追踪对象的变化,这就是我们需要的东西来实现shouldComponentUpdate。这样你就可以获得一个很好的性能提高。

转载于:https://www.cnblogs.com/hahazexia/p/6480085.html

React文档(十八)最佳性能相关推荐

  1. 写好开发者文档的八个技巧

    写好开发者文档的八个技巧 程序员百科 百家号17-05-3120:51 你刚刚收到来自一位愤怒的开发者的电子邮件.你的文档有问题,开发人员花了几个小时才弄明白.现在轮到你更新文档,并找出如何在将来避免 ...

  2. React文档(一)安装

    React是一个灵活的可以用于各种不同项目的框架,你可以用它来写新应用,你也可以逐步将它引进已有的代码库而不用重写整个项目. 试用React 如果你想玩一玩React,那么就去CodePen上试一试. ...

  3. 服务器测评文档,十年磨一剑,腾讯自研TBase数据库有奖测评

    [TBase开源版测评]征文活动获奖名单公布:https://cloud.tencent.com/developer/article/1691427 TBase是 7月13日,TBase重磅发布了开源 ...

  4. Android 系统(157)---ODM 开发用户常见需求文档(八)

    一:信号图标,3G改为H,G改为E (frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/Telep ...

  5. React文档(十四)深入JSX

    根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖.JSX代码是这样的: <MyButton color=&q ...

  6. React文档(十九)不使用ES6

    通常你会将一个React组件定义成一个普通的js类: class Greeting extends React.Component {render() {return <h1>Hello, ...

  7. React文档(十五)使用propTypes进行类型检查

    注意: React.PropTypes 自 React v15.5 起已弃用.请使用 prop-types 库代替. 随着你的应用的开发,你会使用类型检查的方法来捕获很多bug.对于一些应用,你可以使 ...

  8. React文档 state and lifecycle

    状态和生命周期 这篇介绍 React 组件中状态和声明周期的概念.详情可以查看API参考 . 思考前一部分中时钟的例子.渲染元素中,我们仅学习了一种更新 UI 的方式.调用 ReactDOM.rend ...

  9. React文档(六)state和生命周期

    想一下之前的章节时钟的例子. 目前为止我们只学习了一直方式去更新UI. 我们调用ReactDOM.render()方法去改变渲染的输出: function tick() {const element ...

最新文章

  1. win7磁盘清理_window7越来越卡?系统残余文件太多,磁盘清理可以搞定!!
  2. matlab在统计学中的简单应用
  3. MySQL中Myisam、InnoDB碎片优化
  4. Postgre中的 select for update 和 select for update nowait
  5. 公式中的引号怎么输_Excel计数函数中这些奇怪的参数让我百思不得其解!
  6. Word Frequency(Leetcode192)
  7. vs2005下载|中文版|官方
  8. oracle -varchar ,varchar2
  9. 中国金属包装容器制造行业竞争格局分析与投资规划深度研究报告2022-2028年版
  10. 苹果计算机访问限制,苹果手机访问限制密码忘了怎么办
  11. WPS2019 For Ubuntu
  12. 微信视频号怎么运营?实操分享我的30个经验
  13. (转)ibatis Tips 之 java.util.Map作为parameterClass和resultClass
  14. 分类信息网和织梦搬家后报错Fatal error: Uncaught ArgumentCountError: Too few arguments to function AddFilter(), 3
  15. GMSK通信系统中频偏估计改进算法
  16. java如何隐藏sheet,Java 添加、隐藏、删除Excel工作表(基于Spire.Cloud.Sdk for Java)
  17. Mondrian 4 测试的简单demo(Saiku简单测试Schema文件)
  18. Google Earth Engine(GEE)——利用Landsat 5 每一期影像制作动画时序并附带时间属性
  19. c语言 指针不能赋值吗,程序员编程C语言指针答疑:指针赋值没那么简单
  20. 工业智能网关BL110应用之二十一: 如何添加LAN口采集的设备

热门文章

  1. 中职计算机专业英语ppt,计算机专业英语ppt
  2. Ubuntu16.04 安装OMnet++4.6(5.4也可)
  3. GIS系统在房产信息平台中的作用
  4. 弱电怎么控制强电/怎么用单片机来控制交流电220V通断
  5. 张宇线代30讲学习笔记
  6. (C++)模拟图灵机-简单的UN+1和UN*2
  7. shell脚本中命名管道
  8. 搜狐微博改版 走差异化发展之路
  9. crontab Do you want to retry the same edit? (y/n)
  10. 如何做好主题公园旅游规划和体验旅游开发?