nixzheng

2017-06-30 10:56

Why you shouldn`t use Preact, Fast-React, etc. to replace React today

本文作者:IMWeb nixzheng 原文出处:IMWeb社区 未经同意,禁止转载

生命在于折腾,Coder的折腾就在于造各种轮子。React在前端圈大火之后,轮子层出不穷。而其中的一些轮子,由于专注于解决很多人诟病的React过大、过慢的问题(然而不并不觉得),也相当出名。关注最多的莫过于PreactInferno等以「轻量化」为特色的库了,Github Star数也超过10000。另外由于React广泛应用于同构应用上,并且 rendertoStringrenderToStaticMarkup 也存在同步执行、速度慢等问题,一些专注于React服务器端渲染的库也颇受关注。如Preact产出的preact-render-to-string还有我现在的团队在用的fast-react-render,都是这个思路。

然而我一直都不喜欢这些替代库,特别是在同构的场景下。有以下几个简单的原因:

Checksum

React通过 renderToString 生成的DOM Content除了在每个节点上都有 data-reactid 属性外,在根节点上还生成了一个 checksum 的属性。浏览器端的React会在第一次render时校验节点内容与服务器生成内容的一致性,如果不一致则需要做很多DOM的增删改操作。理论上一致的checksum会节省大量JavaScript执行、Reflow时间(可查看Benchmark)。而preact-render-to-string、fast-react-render都没有加入checksum的逻辑。其生成的代码,不管前端使用Preact还是React,都需要将整个DOM树重渲染一次。由于浏览器端的render也是同步的,意味这段重渲染的时间里是无法与页面交互的。

兼容性

Preact, Inferno并没有实现对React的100%兼容(即便使用了xxx-compat包)。姑且不论你可能无法使用React社区里无数针对React深度定制的包,你也无法使用大家都喜欢的ant-design

不做过早优化

「程序员工具箱中最强大的优化技术就是不做优化。」——《Unix编程艺术》

关注我们的软件核心问题是什么,性能瓶颈是什么。网站加载速度慢真的是由于React框架过大?很多工程师往往为了优化而优化,而且结合自身背景只做自己分内的优化,却忘记了优化的最终目的是什么。花更多的时间去解决更关键的问题,而不是花在各种使用替换方案和解决其兼容性上。

可维护性

我个人最喜欢Facebook开源项目的一点是,他们的项目是真的用在了自己的核心产品上。不单依靠那些五花八门的测试用例、没有那些天花乱坠的推广软文,React的稳定性已经得到数十亿网民的验证。而且,Facebook在React 16还没有release的情况下已经在主站开始使用React Fiber,并通过完整的测试用例保证其向后兼容性,让React的使用者很安心。

而其他的项目,多为个人开发的项目。开源社区里过个一两年之后就停止维护的事情也是常有的,这些替代库的bug修复是否及时,是否能跟上React的更新节奏,我个人也是很持怀疑态度的。

面向未来

接上文的React更新节奏说起,React Team一直保持着高效、专注的产出,围绕着提升Web开发体验、表现性能等持续优化。Fiber在调度上做了相当多的优化,实际动画性能需要进一步了解,不过之前饱受诟病的 renderToStringrender 的性能同时也得到了较大的提升(可以在Benchmark中看到),一下子拉近了和其他强调渲染性能的替代者的距离。

另外倍受关注的流式渲染也终于合并到主干了!我们之前甚至考虑过使用react-dom-stream来替换掉 ~renderToString~,这下可以直接使用官方的异步方案了。效果如何不好说,但起码增加了更多可能性。而同时Preact等并没有Stream方案。

再次,从优化的角度来看,框架都是在演化的。今天我们可以为了性能换上Preact,明天也许就要为了性能去换上Xreact。然而不管摩尔定律有没有失效,硬件、网络环境终归是在不断发展的,这些通过换库带来的一些体积、执行时间上的节省,半年、一年之后也许就微乎其微,而我们之前的工作也就没有任何意义。

Benchmark

最后口说无凭,对React、React@15、Fast React、Preact做了一个benchmark。react-static是指调用 renderToStaticMarkup 时的数据。结果如下:

Server

- fast-react preact react react-static react15
1000 times 3.00 4.00 7.00 3.25 7.75
200000 times 311.00 443.00 557.00 297.50 805.50

可以看到React@16相比React@15已经提升了1/3,接近Preact的成绩了。

另外值得注意的是 renderToStaticMarkup 其实性能相当不错。如果你确定可以放弃checksum,希望寻找一个更快的服务器端渲染方式,不如直接使用 renderToStaticMarkup

你也可以clone https://github.com/raxjs/server-side-rendering-comparison 来对比React@16 & React@15,相信会得到类似的结论。

Client

Client是使用Chrome Headless模式来测试的。

Fast-React的浏览器端直接使用React@16来渲染。Preact的render方法因为默认是append行为,所以增加了一个preact-replace来和React行为做对比。

parse时长是调用 renderToString , renderToStaticMarkup, preact.render 方法的时间。load时间为 performance.timing.responseStart 到render结束后的时间。

- fast-react preact preact-replace react react-static react15
1000 times parse: 14.00,load: 79.75 parse: 10.75,load: 39.25 parse: 15.25,load: 34.00 parse: 10.25,load: 79.00 parse: 14.50,load: 80.25 parse: 18.00,load: 93.75
200000 times parse: 1312.00,load: 9219.50 parse: 1805.25,load: 9571.50 parse: 1808.75,load: 9573.50 parse: 708.75,load: 9604.50 parse: 1350.00,load: 9256.25 parse: 2144.25,load: 11130.50

React@16在浏览器端渲染性能提升了一倍多,成绩相当不错。而且得益于checksum的加持,如果checksum是一致的(react栏),React的渲染时间在DOM节点较少的时候与Preact一致,在节点很多的情况下比Preact、其他方案的成绩要好一大截。

而由于Preact体积的优势,在节点较少的情况下load时长相当短,相信在低速网络、低端设备下会有更好的性能表现。

其中还有一个有意思的一点,在200000节点的情况下,虽然React的render时间很短,但最后的load时间却不是最短的。自然是由于每个DOM节点上增加的 data-reactid 导致HTML下载、解析时间变长。不过React Team也考虑过彻底移除data-reactid,也许未来某个版本会得到修改,而那时直接使用React渲染同构应用将肯定是性能最佳的选择之一。

小结

很兴奋React@16在性能方面做了这么多优化,相信绝大多数情况下我已经不用考虑其他的替代方案了。当然也有例外:

  • 当你的目标用户网络环境比较糟糕时:之前的测试主要是基于执行速度来考察的。然而React毕竟有40多K,比起Preact还是要大不少。在开发m.uber里,提到了多次2G网络,我觉得也是影响Uber工程师选择Preact的关键因素。如果你的用户主要使用2G网络,可以考虑一些替代方案。(当然这也是违反我们「面向未来」的观点的,uber工程师也有可能也是为了替换Preact而找的理由……
  • 从零开始一个项目,不喜欢React:这种情况下,请随意……

(完)

4条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。