前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >干货 | Qreact,去哪儿网的迷你react方案

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

作者头像
携程技术
发布2018-03-16 12:04:41
9580
发布2018-03-16 12:04:41
举报
文章被收录于专栏:携程技术携程技术携程技术

作者简介

钟钦成,网名司徒正美,著名的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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 携程技术中心 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档