React高级性能优化
作者:hepeguo 发布时间:[ 2016/8/16 16:12:13 ] 推荐标签:软件测试 性能测试
基本上,我们结束了使用深度对比来确保改变的正确跟踪,这个方法在性能上的花费是很大的,因为我们需要为每个 model 写不同的深度对比代码。算这样,如果我们没有处理好对象引用,它甚至不能工作,比如说这个父组件:
React.createClass({
getInitialState: function() {
return { value: { foo: 'bar' } };
},
onClick: function() {
var value = this.state.value;
value.foo += 'bar'; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
内部组件第一次渲染的时候,它会获取 { foo: 'bar' } 作为 value 的值。如果用户点击了 a 标签,父组件的 state 会更新成 { value: { foo: 'barbar' } },触发内部组件的重新渲染过程,内部组件会收到 { foo: 'barbar' } 作为 value 的新的值。
这里的问题是因为父组件和内部组件共享同一个对象的引用,当对象在 onClick 函数的第二行发生改变的时候,内部组件的属性也发生了改变,所以当重新渲染过程开始,shouldComponentUpdate 被调用的时候,this.props.value.foo 和 nextProps.value.foo是相等的,因为实际上 this.props.value 和 nextProps.value 是同一个对象的引用。
因此,我们会丢失 prop 的改变,缩短重新渲染过程,UI 也不会从 'bar' 更新到 'barbar'
Immutable-js 来救赎
Immutable-js 是 Lee Byron 写的 JavaScript 集合类型的库,近被 Facebook 开源,它通过结构共享提供不可变持久化集合类型。一起看下这些特性的含义:
Immutable: 一旦创建,集合不能再改变。
Persistent: 新的集合类型可以通过之前的集合创建,比如 set 产生改变的集合。创建新的集合之后源集合仍然有效。
Structural Sharing: 新的集合会使用尽量多的源集合的结构,减少复制来节省空间和性能友好。如果新的集合和源集合相等,一般会返回源结构。
不可变让跟踪改变非常简单;每次改变都是产生新的对象,所以我们仅需要对象的引用是否改变,比如这段简单的 JavaScript 代码:
var x = { foo: "bar" };
var y = x;
y.foo = "baz";
x === y; // true
尽管 y 被改变,因为它和 x 引用的是同一个对象,这个对比返回 true。然而,这个代码可以使用 immutable-js 改写如下:
var SomeRecord = Immutable.Record({ foo: null });
var x = new SomeRecord({ foo: 'bar' });
var y = x.set('foo', 'baz');
x === y; // false
这个例子中,因为改变 x 的时候返回了新的引用,我们可以安全的认为 x 已经改变。
脏检测可以作为另外的可行的方式追踪改变,给 setters 一个标示。这个方法的问题是,它强制你使用 setters,而且要写很多额外的代码,影响你的类。或者你可以在改变之前深拷贝对象,然后进行深对比来确定是不是发生了改变。这个方法的问题是,深拷贝和深对比都是很花性能的操作。
因此,不可变数据结构给你提供了一个高效、简洁的方式来跟踪对象的改变,而跟踪改变是实现 shouldComponentUpdate 的关键。所以,如果我们使用 immutable-js 提供的抽象创建 props 和 state 模型,我们可以使用 PureRenderMixin,而且能够获得很好的性能增强。
Immutable-js 和 Flux
如果你在使用 Flux,你应该开始使用 immutable-js 写你的 stores,看一下 full API。
让我们看一个可行的方式,使用不可变数据结构来给消息示例创建数据结构。首先我们要给每个要建模的实体定义一个 Record。Records 仅仅是一个不可变容器,里面保存一系列具体数据:
var User = Immutable.Record({
id: undefined,
name: undefined,
email: undefined
});
var Message = Immutable.Record({
timestamp: new Date(),
sender: undefined,
text: ''
});
Record 方法接收一个对象,来定义字段和对应的默认数据。
消息的 store 可以使用两个 list 来跟踪 users 和 messages:
this.users = Immutable.List();
this.messages = Immutable.List();
实现函数处理每个 payload 类型应该是比较简单的,比如,当 store 看到一个代表新消息的 payload 时,我们创建一个新的 record,并放入消息列表:
this.messages = this.messages.push(new Message({
timestamp: payload.timestamp,
sender: payload.sender,
text: payload.text
});
注意:因为数据结构不可变,我们需要把 push 方法的结果赋给 this.messages。
在 React 里,如果我们也使用 immutable-js 数据结构来保存组件的 state,我门可以把 PureRenderMixin 混入到我门所有的组件来缩短重新渲染回路。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南