Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

赢财富(微信版)目前存在的性能问题--reac性能问题 #8

Open
ghost opened this issue May 24, 2017 · 2 comments
Open

Comments

@ghost
Copy link

ghost commented May 24, 2017

赢财富(微信版)目前存在的性能问题:

关于文件加载和代码逻辑的性能问题,以及数据管理的性能问题,这里暂不讨论。这里,主要讨论react的渲染性能。

这里讨论的react的渲染性能,对于我们目前的赢财富,我暂时没有考虑到比较全面的解决办法,但是也有一点不成熟的想法。下面我将从产生性能问题的原因开始进行说明。

要说明为什么会有渲染性能问题,需要明白react是怎么执行渲染操作的。react组件在接受到新的props或者state的时候,会进入组件的重新渲染过程。

具体的生命周期和渲染流程请查考如下两篇文章:

《ReactJS 生命周期、数据流与事件》:http://www.codeceo.com/article/reactjs-life-circle-event.html

《react组件性能优化探索实践》,详细介绍了渲染过程:http://imweb.io/topic/577512fe732b4107576230b9

在这里我简单的介绍下重新渲染过程:

从这个过程我们可以看到,只要组件接受了新的数据(来自props或者state),默认它都会进入到重新渲染过程,执行render方法,生成虚拟DOM并且执行diff操作。

然而,在有些情况下我们的新的数据并不需要重新渲染,或者新的数据与原来数据的值相同,因此渲染结果也是一样,这时候执行render函数再进行虚拟DOM的diff操作,很明显是很浪费性能。

而解决这种性能损耗的关键就是:通过shouldComponentUpdate来控制是否执行re-render。

在我们赢财富中,目前有三个方面会引发渲染性能损耗,这些性能损耗更多的出现在子组件的re-render上。

由于事件引起的重绘损耗

先看一个最简单的,赢财富历史操作页,如下所示:

这个操作页中,每一个Item(或者叫card)都是messageItem组件的示例,在这里我共有4个Item。他们都是当前页message页的子组件。

在messageItem组件类的render方法中,添加一句Log:

render(){
	console.log('message')	
	...
}

进入历史操作页,查看Log:

首次进入该页面,每个子组件都会被渲染,这里有4个子组件,打印4个Log信息,正确。

但是,如果我们点击这个页面中的空白部分(除4个子组件和底部bar),或者更直接一点进行上拉刷新操作。然后再来查看Log:

what?item并没有增多,实际上每个Item中的数据也没有改变,但是,多了4条Log信息,说明这4个子组件都执行了render()和diff操作。

我们可以设想,如果我们的item信息较多的时候,而每次的上拉操作,这些已经渲染过的所有子组件,还要再进行一遍虚拟DOM生成和diff操作,那将是多么的损耗性能。

引发这种情况的原因,是在历史操作页message组件中,事件监听改变了state的值,从而引发子组件的re-render。

以上是第一个引发性能问题的情况,关于这个的解决办法,我暂时还没有思考,毕竟它不是最麻烦的问题,仅仅是由于事件引发的不必要的渲染。

由于请求等待引起的性能损耗

下面看第二种情况:

这里的Log信息,应该一目了然,现在只需要关心被我标注的Log信息。第一次加载该页面,页面被创建,请求页面数据,4个item子组件被创建成功。

然后我们切换到历史操作页,再切换回到签约服务页。切换回来我们看到的是这样:

在切换回签约页这一瞬间,实际上我们的页面应该是和开头那幅图看到的结果一样的。因为,此时我们的store中的数据和之前是一样的,而且所有的子组件也被创建了。

关键点来了,所有页面正常渲染完成之后,再次发起请求,请求最新的状态等信息。所以,这时候,就加载了一个跳动的圆这个组件,而之前已经创建好的item子组件都被移除了。而且,这里我们就一直处于等待状态。

当请求返回时,结果如下:

而请求返回后,所有的子组件会再次被重新渲染。此时,各个子组件的状态实际上跟之前绝大多数情况下都没有改变,或者有轻微变化。

比较极端的情况是,比如刚点击完签约,服务端还未返回,此时store中的数据状态还未来得及改变,页面也还未刷新,然后马上就跳转到其他页,再转回来。此时各个子组件的状态首先使用原来的老数据渲染,这时候请求返回,然后各个子组件的状态是会改变的。

在这里为了体现正在刷新,从而用了一个跳动的圆,并且还把已经渲染完成的组件移除。造成了两个问题:

  1. 用户一直处在等待状态,即使之前已经加载过的内容他也完全看不见,用户体验不好。
  2. 组件可能只有轻微的状态变化,但是组件经历移除和重新创建,造成了更大的性能损耗。

更改建议:初始加载页面可以不变,使用跳动的圆表示等待。当下一次进入该页面,请求刷新过程,不再使用跳动圆,直接呈现之前的状态,在其他位置使用刷新提示,比如4个item下面,从而告诉用户正在刷新。为了避免刷新过程中用户误操作,可以禁用签约按钮。

可变数据造成的性能损耗

可变数据造成的性能损耗充斥在赢财富的各个页面中。其实,前面两种,也可以归结为可变数据造成的。

看个例子:

这是第一次加载页面,所有的数据均加载完成。4个item的数据来自当前product对象的results数组。

点击“价值型”产品的“已签约”进行,退签:

点击确定后,会发送请求到服务端。这里有个发起请求和收到请求两个过程,因此会有两个刷新子组件的过程:

但是,注意了,这里是4个子组件均进行了render和虚拟DOM的diff。而实际上,我们results数组中的值,只有第一个被改变了,其余三个的值并没有变化。也就是说,另外三个组件的render和虚拟DOM的diff是浪费的。

造成这个问题的原因就是可变数据,JS中数组是引用类型,实际上我们我们的每次操作,都会results进行重新复制,所以,每一次results都是一个新的引用。正是这种可变数据造成了页面太多的性能损耗。

关于解决可变数据带来的性能损耗,社区比较普遍的做法是使用immutable.js

但是,对于目前的赢财富,并不适合:

  1. immutablejs太大了,达到了50K之多
  2. 赢财富中的数据结构较复杂,页面较多,状态较多,并不能轻易的使用。况且,我也还没仔细考虑过,是不是完全适合。

关于可变数据,造成的问题,我还需要更多的考虑和讨论。

下面列举一些,我目前正在学习的参考资料:

  1. react组件性能优化探索实践http://imweb.io/topic/577512fe732b4107576230b9
  2. 不可变数据的辅助工具(ImmutabilityHelpers)http://lib.csdn.net/article/react/17600
  3. 另外一个不可变数据插件https://github.com/debitoor/dot-prop-immutable
  4. Immutable 详解及 React 中实践Immutable 详解及 React 中实践 camsong/blog#3
  5. 谈谈React的Flux架构和Immutable.jshttp://www.cocoachina.com/webapp/20150902/13316.html
  6. React移动web极致优化React移动web极致优化 lcxfs1991/blog#8
  7. 如何用React+Redux+ImmutableJS进行SPA开发http://yunlaiwu.github.io/blog/2016/12/01/react+redux+immutablejs/#2
  8. cortex,我很看好这个,但是还不知道怎么和redux一起用https://github.com/mquan/cortex
  9. immutability-helperhttps://github.com/kolodny/immutability-helper/tree/react-immutability-helpers-readme
  10. immutable-jshttps://github.com/facebook/immutable-js
  11. React高级性能优化https://segmentfault.com/a/1190000006254212
  12. react如何性能达到最大化(前传),暨react为啥非得使用immutable.jshttps://segmentfault.com/a/1190000004290333
  13. Redux性能优化https://segmentfault.com/a/1190000006110864
  14. React组件性能优化https://segmentfault.com/a/1190000006100489
  15. React-Redux性能优化https://segmentfault.com/a/1190000006120707
  16. React+Redux 性能优化实践https://segmentfault.com/a/1190000007189896
@MinJieLiu
Copy link

immutablejs 大么 50多k不是问题,现在流量1元500m😀

@MinJieLiu
Copy link

你也可以不必一开始就全部换成 immutablejs, 局部使用也是可以的,好用的话再慢慢改造以前的代码

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant