干货 | Qreact,去哪儿网的迷你react方案

作者简介

钟钦成,网名司徒正美,著名的JavaScript专家,去哪儿网前端架构师。在GITHUB拥有复数个著名的轮子,著有《javascript框架设计》一书。本文来自司徒正美在“携程技术沙龙——新一代前端技术实践”上的分享。

*视频由“IT大咖说”提供,时长约45分钟,请在WiFi环境下观看*

去哪儿网在React Native深耕多年,对React内部实现的了解在国内应该是非常领先的。迫于项目对React体积的极致需求,我们推出了自己的迷你化方案——Qreact。

Qreact比市面上的其他迷你react框架的实用性更强,基本上可以说无缝切换到任何已有的react 15工程中,能极大地改善对体积的压力,这在对流量非常苛刻的移动端上尤其重要!

我们从今年1月份快速启动项目,在1个月内大致完成了功能,Demo,并配合现有的复杂例子进行验收。本文将分享我们在做这轮子过程中的一些想法,包括竞品分析,实现思路,项目风险控制等等。

一、核心需求

我们并不是无事找事,为造轮子而造轮子。虽然有KPI的成分,但它的核心需求是来自业务线,可以说就算我们不造,公司内部其他人也会造一个,但那个质量可能无法保证,毕竟公司绝大部分的高手都分配到我们事业部。

由于没有产品经理,我们需要充当产品经理的角色,聆听业务线的需求,自己挖掘需求。

去哪儿深耕React多年,构建了两个基于React的UI库,它们都是用于移动端。如果这些库都是内置在APP中,应该没有要求。但是去哪儿分成十来个事业部,根据事业部的赚钱能力分配更新包的体积。为了能让用户在wifi上更新我们的APP,更新包的体积一般不超过100MB,因此像这样公用的框架与库体积越少越好。

此外,其中一个UI库是用于手机浏览器上,我们称之为React Web,用户每次打开我们的页面,都会加载一遍React与相关组件,这个对体积就更加敏感。因此当我们完成React Web,就着眼于迷你React的开发。

这个新的框架有三个核心需求:

1、体积小。移动端对体积一向敏感,因此在jQuery时代,zepto能割踞一方。

2、支持事件系统,这不是简单add Event Listener,是React原来的那套Synthetic Event。它帮我们搞定300ms延迟,还有滚动列表时误触发点击的问题。如果你不用它,你需要让业务线参照iscroll的原理自造一个。

3、能直接替换。换言之,新框架与原框架的功能几乎一致。因此许多业务已经

用React开发完毕,不希望做太多改动。由于业务线有时时间赶,碰到难题搞不定,会倾向用一些怪招歪招。在无法预料对方用什么API的情况,新框架框架覆盖原React的各种偏门用法。

下面是一个紧急修复的补丁:

图1

我们列举一下各种偏门的API与用法

1、mixin包含mixin。这个在RN很常见。

2、ref, setState传函数的用法

3、context与getChildContext的运用,虽然官方明确不建议大家用,但是著名的react-redux在源码里用到了。

4、_rootNodeID, _hostParent,_hostNode这些内部属性用在后端渲染与事件系统中。

二、竞品分析

图2

在立项后,我们开始找市场上的同类产品,如果有满足的,我们就不用开发了。目前,前端要找这些框架,只有一个去处,就是GITHUB。这是开源界的宝库,应有尽有,琳琅满目。

由于代码公开,大家可以抄抄,因此每流行一样的东西,大家都是一窝蜂上的。除开那些纯练手的项目,每个库都有自己独到之处。

自从React推出虚拟DOM来解决复杂应用的性能问题以来,GITHUB上有上百个虚拟DOM的库,包括之前的angular, vue2都在底层使用这种性能利器。

图3

这是一些虚拟DOM框架或库的数据,从相似度,性能,流行度,版本更新等情况综合考虑,我们也只能选上面三者:inferno, preact, react-lite。

inferno从各方面来看,是无可挑剔的,性能比排行第二的kivi快20倍,更不用说vue,angular什么之流。每个库都会吹自己的框架有多快,但inferno的主页上有大量测试页面,是有真实数据支撑的。但是它偏面追求性能,源码里的可读性太差。看不懂,无从入手,只能遗憾地放弃了。

其他性能流有citijs, snabbdom, virtual-dom。最早搞出性能引擎的是citijs,然后基于它上面分化出kivi, ivi, snabbdom,然后vue2.0又直接将snabbdom库整合到它里面。virtual-dom则是走另一种性能优化方式。但它们都是迷你库,API与React差太远。

于是只剩下preact与react-lite。

三、设计思路

由于是业务线的迫切需求,并且拖得越久,就越多项目用上RN,到时需要回归测试的项目就越多,因此必须尽快搞出来。我们就不打算重造轮子,而是在已有轮子上改改。

第一版是基于react-lite。这是因为react-lite是携程的工业聚大神写的,携程是我们的兄弟公司,应该比较好交流。但现实中发现,这个库的扩展性不足,比如说事件系统那里,需要传入4个参数,在react-lite里只能拿到三个参数,想尽方法也无法凑齐第四参数。还有一些内部属性,渲染流程与原装React差得太远了。在双方折腾了2个星期后,我们组有人心灰意冷,着手后备方案,preact。

preact比起react-lite多出几个优势:

1、官方提供兼容补丁preact-compat

2、插件巨多

3、ISSUR活跃,当天提问题,大概到晚上,外国人起床就有回应了。

4、扩展方便

尤其第4点,在开发qreact时,我们都为双方提了不少ISSUE。其实程序员还是比较腼腆,不愿麻烦人,因此我们写框架时还是多留一些扩展接口吧。

整个qreact的架构大概就是:

qreact= preact改+preact-compat改+react-web事件系统迷你版

在preact的源码里一个叫options.js的文件,里面有一个options的对象,它会被框架的多个关键方法调用。我们通过为它重新实现某些方法,就达到改写框架的目标。

https://github.com/developit/preact/blob/master/src/options.js

图4

两个react-lite的难点问题,由于options的扩展机制太灵活了,一下子被摆平。

1、事件系统需要传入4个参数的问题。在options添加一个handle Event方法。

2、内置属性问题,在options重写vnode方法。

重点说一下内部属性问题:

图5

随着版本的升级,这些内部属性越来越多,这里讲解一下其中三个:

图6

这了让preact支持它们,我们是在框架diff节点时,重新添加上它们的。因为这时,我们能轻松知道一个节点在DOM树的上下关系。

最后是对事件系统进行瘦身。React有16000行,其中10000行都是事件系统相关的。再加上React Native中的Pan Responder系统。这体积非常庞大。但是如果我们将要支持的浏览器收窄一点,不支持IE系列与firefox系列。起码在事件对象的构造器上,我们可以做一些合并操作。

下面React中的事件构造器列表:

https://github.com/facebook/react/tree/v15.3.2/src/renderers/dom/client/syntheticEvents

它们浓缩成一个事件构造器后,代码少了3000行。

我们再对事件插件进行围剿。因为我们不需要mouseenter, mouseleave, input, composition,beforeinput的兼容,又可以减少许多行。

https://github.com/facebook/react/tree/v15.3.2/src/renderers/dom/client/eventPlugins

最后成果是 qreact缩少到6000行,事件系统占其中的4000行,min后的体积为39kb。原版React的min体积是140kb。减少近80kb。

体积算是达标了,那么性能如何呢?毕竟我们使用React的初衷是因为它的性能太好了。React的性能主要来自它的虚拟DOM的diff算法。体积缩水了,它的diff算法肯定也打折扣。这时preact提出两个后备方案:

1、减少要比较的虚拟DOM的数量 hydrate。这是发端于inferno的优化方案,通过合并相邻的字符串或数字,减少虚拟DOM数量。

图7

2、减少要生成的真实DOM的数据recycle。上面的hycycle也会减少真实DOM的数量,但我们还可以将要移除的真实DOM保存起来,重复利用这些真实DOM。

通过这两种机制,大大弥补qreact diff算法的缺憾。此外,我们还可以通过动静分离的方式来提高性能。在定义JSX时,我们就能得知某个元素是否包含花括号,有花括号说明其是动态的,反之是静态的,但一个元素与其所有子孙都没有花括号,那么这个子树可以整体缓存起来,以后转换为真实DOM后,它能缓存起来。

然后在组件的render方法中,对于这部分的React Element每次返回相同的对象,并且在上面添加一个标记,碰到两个对象都有这个标记,就直接返回,不往下比较了。这是inferno提出的另一个性能优化方案。

最后验证性能是用ListView进行测试的,和原来一样流畅。

四、分享展示

里面最重要的两个例子就是yo-demo与qunar-react-native-web

原文发布于微信公众号 - 携程技术中心(ctriptech)

原文发表时间:2017-03-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏全栈工程师成长之路

全栈开发自学日志(持续更新)

32470
来自专栏互联网杂技

JavaScript就要统治世界了?

" JavaScript 可以……" "嘛,不就是操作一下 DOM,可以让元素飞来飞去吗" "JavaScript 是……" "不就是用 jQuery 让网页动...

35260
来自专栏前端知识铺

入口文件开始,分析Vue源码实现

网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行。

417120
来自专栏顶级程序员

Android学习路上会遇到的各种瓶颈总结

前言 对于大多数大三学生来说,这个暑假是人生最后一个暑假。对于IT专业的学生来说,开学后就要面对各大IT企业的秋招,很多人会成为从0开始的An...

48980
来自专栏IMWeb前端团队

关于Uinicode的一个“有趣的”事情

本文作者:IMWeb 黄龙 原文出处:IMWeb社区 未经同意,禁止转载 故事是这样开始的 产品K: 为什么我们的网站不能显示火星文? 开发L: 什么样的火星文...

22850
来自专栏互联网杂技

帮你找好图!99%的互联网从业者都要学会的图片搜寻方法

收藏了十几个图库,依然搜不到好图片?那就别错过今天这个教程!这个「图片搜索」系列,不仅教你学会如何榨干一个优秀的图库,而且还附上了那些无法另存的图片的下载方法,...

30290
来自专栏web编程技术分享

flex布局方法详解之flex-wrap

17330
来自专栏HT

从IE6到IE11上运行WebGL 3D遇到的各种坑

这篇《基于HTML5的电信网管3D机房监控应用》基于WebGL技术的应用让少同学对HTML5 3D的应用产生了兴趣和信心,但有不少网友私信询问WebGL如何运行...

25750
来自专栏web前端教室

javascript es6 和 es5,学哪个呢?

以下是我个人看法,全部都是片面的,请不要相信。 ES6 确实是在JS本身上添加了一些新东西,但真心不多,而且现在各个浏览器对它的支持还有待提高。 那是不是就不学...

28590
来自专栏IT平头哥联盟

小程序项目之再填坑记

  是的,真的,你没有看错,我就是上次那个加薪的,但是现在问题来了,最近又搞了个小程序的需求,又填了不少坑,其中的辛酸就不说了,说多了都是泪??,此处省略三千字...

13330

扫码关注云+社区

领取腾讯云代金券