React学习(7)—— 高阶应用:性能优化
性能优化在React内部已经使用了许多巧妙的技术来最小化由于Dom变更导致UI渲染所耗费的时间。对于很多应用来说,使用React后无需太多工作就会让客户端执行性能有质的提升。然而,还是很其他更多的办法来加速React程序。 使用生产模式来构建应用如果在开发和使用的过程中感觉了React应用有明显的性能问题,请先确认是否已经构建了压缩后的生产包:
切记不要将开发模式的包发布到生产环境,因为开发包中额外包含了许多用于辅助的测试的信息,无论在加载还是执行时,它都比较慢。 使用chrome分析组件的渲染时间线在开发模式中下你可以直接在chrome的性能工具中看到组件是如何装载、更新和卸载的。例如下面的图片展示的效果: 在chrome中按照以下步骤执行:
关于分析的数据,需要明确的是:渲染的时间只是一个相对的参考值,在构建成生产包之后,渲染的速度会更快。尽管如此,这些数据仍然能够帮助我们分析是否有不相关的UI被错误的更新,以及UI更新的频率和深度。 目前只有Chrome、Edge和IE支持这个特性,但是官方正在使用User Timing API标准让更多浏览器支持这个特性。 手工避免重复渲染React构建和维护了一个内部的虚拟Dom,这个Dom和真实的UI是相互映射的关系,他包含从用户自定义组件中返回的各种React元素。这个虚拟的Dom使得React可以避免重复渲染相同的Dom节点并在访问存在的节点时直接使用React的虚拟层数据,这样设计的原因是重复渲染浏览器或web view的UI比操作一个JavaScript的对象要慢许多。在React Native也采用同样的处理方式。 当组件的props和state变更时,React会将最新返回的元素与之前旧的元素进行对比来确定是否真的需要重新渲染真实的Dom。当他们不相等时,React会更新真实的Dom。 在某些情况下,可以在自定义组件中重载 shouldComponentUpdate(nextProps,nextState) { return true; } 如果在某些情况下能够清晰的明确组件不需要重新渲染,可以在 shouldComponentUpdate 的执行过程下面是一个组件结构树。图中,“SCU”表示 在C2组件中, 对于C1和C3, 还有一个值得关心的组件是C8,React在这个组件中执行了render()方法,但是由于虚拟Dom并没有发生变更,前后比对一致,所以并没有发生真实Dom渲染。 在整个过程中React仅仅变更了C6组件的UI样式,C8由于前后虚拟Dom一致因此没有真正的执行UI渲染。C2、C2的子组件以及C7没有执行render()方法。 一个shouldComponentUpdate的例子在例子中,当props.color和state.count发生变更时进行UI渲染,我们在 class CounterButton extends React.Component { constructor(props) { super(props); this.state = {count: 1}; } shouldComponentUpdate(nextProps,nextState) { //只判断props.color和nextState.count是否变更,其他情况均不渲染 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> ); } } 在这段代码中, //继承自React.PureComponent 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> ); } } 在大部分情况下,只要使用 附:数据突变(mutated)是指变量的引用没有改变(指针地址未改变),但是引用指向的数据发生了变化(指针指向的数据发生变更)。例如const x = {foo:'foo'}。x.foo='none' 就是一个突变。 在更复杂的数据结构中还会存在一些问题。例如下面的代码,我们希望 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() { // 这段内容会导致代码不按照预期工作。 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> ); } } 导致代码无法正常工作的原因是 非突变数据的价值有一个简单的方法预防上面提到的问题,就是在使用prop和state时防止数据发生突变。例如下面的例如,我们用数组的concat方法来代替等号“=”,这样在concat后会产生一个新的数组赋值给this.state.words: handleClick() { this.setState(prevState => ({ words: prevState.words.concat(['marklar']) })); } ES6支持列表扩展语法,因此我们更容易在es6中实现非突变的数据赋值,例如: 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方案是使用 扩展传播特性(见 object spread properties)来解决数据突变问题,实现如下: function updateColorMap(colormap) { return {...colormap,right: 'blue'}; } 如果是构建React的App应用,那么以上方法都能够很好的支持,如果是在浏览器环境使用,需要引入polyfill机制。 使用不可变的数据结构Immutable.js是解决数据突变问题的另外一种解决方案。它提供不可变、持久化的集合。集合包含下列结构:
数据结构不可变的特性使跟踪数据变化变得很简单。任何变更将始终导致创建一个新的对象,所以我们只需要检查引用(指针地址)是否已经被修改即可确定数据是否已经修改。例如在常规的JavaScript代码中: 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-immutableandimmutability-helper。 不可变的数据结构为我们跟踪数据对象变更提供了更加简便的方式,这是我们快速实现 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |