React高级指南(五)【Optimizing Performance】
性能优化在更新UI时,React内部使用了多种技术最小化必要的DOM操作。对于大多数应用,使用React不需要额外的特定性能优化的情况下,就可以达到一个更快的用户交互。然而,下面有几种方式能够加快你的React应用。 使用生产环境如果你在React应用中遇到性能的瓶颈,请确保你是在生产环境下测试。 默认地,React包含众多的帮助性的警告(warning)。这些警告在开发模式中非常有用。而然它们使得React体积庞大并性能下降,因此,你需要确保你是在生产模式下部署应用。 如果你不确定你部署的模式是否正确,你可以在Chrome中安装React Developer Tools for Chrome。如果你访问的网站是React生产模式,图标背景是深色: 如果你访问的网站是React开发模式,图标的背景将会是红色: 正常情况下,你会在开发过程中使用开发模式,当给用户部署应用使用生产模式。 Create React App如果你的工程使用Create React App,运行: npm run build
这将会在你的工程中 记住,这是指针对于部署产品。对于普通的开发者,使用 Single-File Builds我们提供了生产模式的React和React DOM的文件: <script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
需要记住的是以 Brunch如果使用的高效地Brunch构建,安装uglify-js-brunch插件: # 使用npm
npm install --save-dev uglify-js-brunch
# 使用yarn
yarn add --dev uglify-js-brunch
然后,通过给 Browserify对于使用的高效地Browserify构建,安装下列插件: # 使用npm
npm install --save-dev bundle-collapser envify uglify-js uglifyify
# 使用Yarn
yarn add --dev bundle-collapser envify uglify-js uglifyify
为了创建生产模式的应用,确实你添加了下列的转化规则(顺序很重要):
例如: browserify ./index.js
-g [ envify --NODE_ENV production ] -g uglifyify -p bundle-collapser/plugin | uglifyjs --compress --mangle > ./bundle.js
Rollup对于使用的高效地Rollupy构建,安装下列插件: # 如果使用的是npm
npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-uglify
# 如果使用的yarn
yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-uglify
为了创建生产模式的应用,确实你添加了下列插件(顺序很重要):
plugins: [
// ...
require('rollup-plugin-replace')({
'process.env.NODE_ENV': JSON.stringify('production')
}),require('rollup-plugin-commonjs')(),require('rollup-plugin-uglify')(),// ...
]
完整的例子查看gist 记住你仅需要在生产模式下使用,你不应该在开发模式下使用 Webpack
对于使用最高效地Rollupy构建,确保在生产配置下添加下面插件: new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),new webpack.optimize.UglifyJsPlugin()
更多的信息可以了解Webpack文档 记住你仅需要在生产模式下使用。你不应该在开发模式下使用 使用Chrome Timeline分析组件性能在开发模式中,你可以在支持相关功能的浏览器中使用性能工具来可视化组件安装(mount),更新(update)和卸载(unmount)的各个过程。例如: 在Chrome操作如下:
注意,上述数据是相对的,组件会在生产环境中有更好的性能。然而,这对你分析由于错误导致不相关的组件的更新、分析组件更新的深度和频率很有帮助。 目前Chrome,Edge和IE支持该特性,但是我们使用了标准的 User Timing API,因此我们期待将来会有更多的浏览器支持。 避免ReconciliationReact创建和维护了渲染UI的内部状态。其包括了组件返回的React元素。这些内部状态使得React只有在必要的情况下才会创建DOM节点和访问存在DOM节点,因为对JavaScript对象的操作是比DOM操作更快。这被称为”虚拟DOM”,React Native也基于上述原理。 当组件的 在部分场景下,组件可以通过重写生命周期函数 shouldComponentUpdate(nextProps,nextState) {
return true;
}
如果你的组件在部分场景下不需要更行,你可以在 shouldComponentUpdate下面有一个组件子树,其中 因为以C2为根节点的子树 对于C1和C3, 最后一个值得注意的例子是C8.React必须渲染这个组件,但是由于返回的React元素与之前渲染的元素相比是相同的,因此不需要更新DOM节点。 注意,React仅仅需要修改C6的DOM,这是必须的。对于C8来讲,通过比较渲染元素被剔除,对于C2子树和C7,因为 例子仅当 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 (
<button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> ); } }
在上面的代码中, class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button color={this.props.color} onClick={() => this.setState(state => ({count: state.count + 1}))}> Count: {this.state.count} </button> ); } }
大多数情况下,你可以使用 如果props和state属性存在更复杂的数据结构,这可能是一个问题。例如,我们编写一个 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 bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div> <button onClick={this.handleClick} /> <ListOfWords words={this.state.words} /> </div> ); } }
问题是 不可变数据的力量避免这类问题最简单的方法是不要突变(mutate)props和state的值。例如,上述 handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}
ES6对于数组支持展开语法 ,使得解决上述问题更加简单。如果你使用的是Create React App,默认支持该语法。 handleClick() {
this.setState(prevState => ({
words: [...prevState.words,'marklar'],}));
};
你可以以一种简单的方式重写上述代码,使得改变对象的同时不会突变对象,例如,如果有一个 function updateColorMap(colormap) {
colormap.right = 'blue';
}
在不突变原来的对象的条件下实现上面的要求,我们可以使用Object.assign方法: function updateColorMap(colormap) {
return Object.assign({},colormap,{right: 'blue'});
}
JavaScript提案添加了对象展开符,能够更简单地更新对象而不突变对象。 function updateColorMap(colormap) {
return {...colormap,right: 'blue'};
}
如果你使用的是Create React App, 使用Immutable 数据结构Immutable.js 是解决上述问题的另外一个方法,其提供了通过结构共享实现(Structural Sharing)地不可变的(Immutable)、持久的(Persistent)集合:
不可变性使得追踪改变非常容易。改变会产生新的对象,因此我们仅需要检查对象的引用是否改变。例如,下面是普通的JavaScript代码: const x = { foo: 'bar' };
const y = x;
y.foo = 'baz';
x === y; // true
虽然 const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo','baz');
x === y; // false
在这个例子中,因为当改变x时返回新的引用,我们可以确信地判定 其他两个可以帮助我们使用不可变数据的库分别是:seamless-immutable和immutability-helper. 不可变数据提供了一种更简单的方式来追踪对象的改变,这正是我们实现 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |