InfoQ|React从入门到放弃,一个关于网页速度的故事
2011 年的时候 , 我碰巧找到一份编写 Backbone.jsapp 的工作 。 如果你从没做过那样的工作 , 千万别去做 。 我会给那些愿意听我抱怨的人 , 喋喋不休地讲我遇到的各种困难 。 当我开始深入研究前端的替代方案时 , 我发现了 FRP、Flapjax 和 ClojureScript 。 其中 ClojureScript 让我迷上了 Clojure 。 我甚至针对 FRP 和 ClojureScript (以及 Hoplon 的前身 hlisp)做了一场 成功的演讲 。
1React然后在 2013 年的时候 , React 发布了 。 我在我的新工作中尝试了 React , 并在 Clojure 主题的峰会(Clojure Cup 2013)期间发现 CLJS 和 React 简直是天作之合 。 React 为什么这么好呢?对于我来说 , 主要的卖点是它组件化做的非常好 。
当你使用之前的东西 , 例如 jQuery、Backbone、Angular 或者其它东西 , 只需一年的开发之后 , 你的代码就会是一团事件监听器和触发器 。 千万别让我碰那些莫名其妙的 JS , jQuery 根本就不存在代码封装 。 哪个处理器在哪里绑定 , 用来做什么?很难说这是一个好的基础库!
然后我开始在 Kasta 工作 , 那里的 web 前端完全就是一团 jQuery 式的玩意儿 。 没人想要碰那些代码 , 因为你会花费数小时 , 甚至数天 , 来做一些最小的改动 。 然后 QA 会发现比你想象的多得多的错误状态 。 然后用户会向我们的呼叫中心报告更多的错误 。 那糟糕程度简直超乎你的想象 。
因此 , 在进行了一些实验、测试和检查后 , 我决定使用 React + ClojureScript 技术栈 , 用 Clojure 进行服务器端渲染 。
2没落有一段时间 , 一切看起来都很好 。 我们的有这样的 架构:我们的组件在后端作为 Clojure 执行 , 所以没有服务器端 Node.js , 太棒了!而且 UX 开发者通过出色的实时重新加载(多亏了 CLJS) , 能够从编辑器连接到浏览器的 REPL , 并直接在编辑器试验 。 那简直太棒了!
长话短说 , 我们的前端变得越来越大 。 增量编译变得越来越慢——现在通常需要一到两秒以上 。 虽然我们做了一些尝试来保持整个 app 的性能 , 但最终我们还是失败了 。 这是一个痛苦的凌迟过程 。 应用程序变得太大 , 启动时间变得太长 。 服务端渲染只能帮助一部分 , 但是混合渲染会阻塞浏览器 。 在比较老旧的硬件或 Android 系统上 , 这变得不可接受!
2016 年的时候 , 一个主要原因是我们在启动时间上采取了大的改动 , 拥有了一个没有页面加载且具有大量交互的富 web 应用程序 。 在一段时间内 , 这是有效的!但是启动时间越来越长 , 导致在谷歌的 PageSpeed 上被评为可耻的 5/100(有时会达到 25/100 左右) 。
除此之外 , 在执行下面描述的操作时 , 我们发现 React 也会导致一些有问题的实践 。 例如悬停用 JS(而不是用 CSS) , 下拉菜单用 JS , 不渲染(在悬停时)隐藏的文本(谷歌对此会不高兴) , 奇怪的复杂逻辑等等 。 你可以创建一个没有这些问题的 React 应用程序 , 但是显然 , 你必须比我们有更好的自控能力(人无完人!) 。
而且从那之后 , 我们的绝大部分用户都转向了移动 app 。 这使得 web app 成为新用户的主要入口 。 这意味着它的主要目标是为新用户快速渲染页面 , 因为想要更多功能的老用户现在都在移动 app 上了 。 而 TTI(响应时间)在这里变得更加重要 。
3变革时刻既然情况已经改变 , 那我们要怎么做呢?在 React 出现之前 , 我读过“我如何靠 vanilla JS 生存”这类的文章 , 但这些文章通常没有任何意义——它要么是一个关于它有多伟大的喧嚣 , 而无视所有问题(关注点分散、内聚性、组件化、代码封装) , 要么是一个(或几个)人在脑袋里想想而没有实际代码的项目 。
早在二月的某个时候 , 我偶然发现了 Intercooler.js 。 我不确定我以前是否见过它——也许我看过但一瞥而过——但这没关系 。 这一次 , 它引起了我的注意 。
其理念是 , 所有的 HTML 都在服务器端渲染 。 而客户端根据元素的属性 , 更新部分 HTML 。 基本上类似 HTML+XHR 。 你不能任意妄为 , 但这是其重点之一;有些限制是好的 , 从而让你不会做一些疯狂的事情 。 你需要服务器的一些支持 , 这样你就可以渲染部分结果——虽然只是一个优化 , 但确实非常重要 。
还有一个备选库——Unpoly 。 这个库围绕布局和样式有更多特性 , 但是在 XHR 上的想法比较少(例如 , 如果没有表单 , 很难完成带参数的 POST 请求) 。 而且这个库大的多 。 而且它是用 CoffeeScript 编写的 , 有许多类 , 这也有点问题 。
所以我用 Intercooler 对我们的目录页做了一个概念验证式的实现 , 而且它成功了!除了对 jQuery 有依赖和其它一些令人恼火的东西外... 当我纠结于对 HTML 片段的请求时 , 我明白了一件事:当我为目录页选择技术路线图时 , 最后的选择是“类似 intercooler 的小东西” 。
那为什么还不行动呢?
4TwinSpark我喜欢 Intercooler 在处理 AJAX 方面的流畅方案 , 所以我决定用一些汽车方面的东西来命名这个库 , 而 TwinSpark 似乎是一个不错的名字 。 这具体是个什么东西呢?
TwinSpark 是一个用于声明式 HTML 增强的框架:你在元素上添加额外的属性 , TwinSpark 对它们对一些处理 。 例如发起一个 AJAX 调用并用响应替换目标 , 或者增加一个样式类 , 或者 , 你们自己看看这些 例子 吧 。
当然 , 它与 Intercooler 有些不同之处 , 不然它为什么会存在?最值得一提的就是 , 它不依赖 jQuery 。 它只支持现代浏览器(不支持 IE 和 Opera Mini)而抛弃掉了 88kb 的兼容性怪代码 。
老实说 , 最主要的原因是 批处理 和 无继承性 。 继承在这里尤其痛苦 。 在 Intercooler 中 , 如果你在 body 中声明ic-target属性 , 其中的所有标签都会认为它们的 target 也是这个 。 这样的话 , 在 HTML 树的某个地方有一个组件 , 而树上更高位置的一个属性改变了这个组件的行为 。 我认为这是一个奇怪的动态范围 , 我可不想要那样!:)有趣的是 , 在对 TwinSpark 进行了一个月的尝试之后 , Intercooler 的作者宣布它在做一个没有 jQuery 的现代版本:htmx 。 它有非常好的扩展点 , 而且可能会增加批处理... 但是仍然有继承 。
5TwinSpark 为什么是一个好主意我们需要从两个方面看待它:它是否对开发者友好以及它是否对用户友好 。 React 对前者优化 , 而对于后者来说是非常讨厌的 。
TwinSpark 方案在大部分情况下对用户更友好:更少的 JavaScript , 更少的抖动 , 更常见的类似 HTML 的行为 。 在最差的情况下 , 我们将返回 2.5MB 简化后的(但没有 gzip 压缩过的)JS 和 700KB 的目录 HTML(其中一半是 React 的初始化数据) 。 JS 包那么大不是因为包含的图像、css 或其它一些复杂的东西 , 而是因为它是整个 app , 有很多视图和逻辑 。
现在它是 40KB 的简化的没有 gzip 压缩过的 JS(TwinSpark、分析埋点、一些行为代码和 IntersectionObserver 兼容包)和 350KB 的 HTML 。 两个数量级的差距 , 而且 HTML 也更小!
在开发者方面 , 我认为 React 仍然比较好些 , 但是 TwinSpark 比用 jQuery 在代码封装和组件化方面要好 。 另外还有很多方法来提升它 。
好消息是开发流程没有太大变化!我们仍然编写从站点内存存储中查询必要数据(需要的时候发起一个 API 调用)的组件 , 但他们只在服务端执行 。 我们在我们以前的架构中有效地利用了后端 , 而且这使得我们能够完美地渲染“部分”HTML——因为组件不需要等待某个“controller”来给他们所有必需的数据 。 这也使得我们可以同时拥有 React 和非 React 版本 , 进行 A/B 测试 , 而无需编写二次标签 。
6结论从首次尝试到发布 , 我们花了 4 个月时间 。 并不是我们刚开始时预想所需的时间(“应该最多需要两三周!”) , 呵呵 , 但并不是只有我们这么做 。 从代码中移除 React 相关代码并将我们的 app 打造成一个服务端应用程序仍然花费了很多时间和精力 。 它仍然需要一些润色 , 但我们还是决定发布它来缩短时间 。 A/B 测试显示我们是对的——特别是对于 Android 手机 。
谷歌现在给我们的目录页排名 75/100 而不是 5/100 。 我想 , 这还是很不错的吧?
作者介绍:
Alexander Solovyov现在是Kasta公司的首席产品官 , 之前是那里的 CTO 。 我们致力于为消费者和生厂商构建一站式电子商务平台 。
参考阅读:
A tale of webpage speed, or throwing away React
本周好文推荐InfoQ 写作平台欢迎所有热爱技术、热爱创作、热爱分享的内容创作者入驻!
还有更多超值活动等你来!
填写申请 , 成为作者
开启你的创作之路吧~
本文插图
【InfoQ|React从入门到放弃,一个关于网页速度的故事】点个在看少个 bug ??
推荐阅读
- 从小就馋此口,比肉香多了,几块钱做一大盘,咋吃都不腻
- 这早餐我从3岁开始吃,三十多年了,从没吃腻过,晶莹剔透很好吃
- 自从学会做这个,我家隔三差五吃,拌一拌上锅一蒸,蘸着料吃,老香了
- 听力障碍|预防耳聋从身边小事做起
- 自从学会这道肉卷,我家隔三差五吃,一卷一蒸,不用蘸酱也好吃
- 我家零食从来不用买,切一切锅里一炸,比油条薯片好吃,越嚼越香
- 馄饨的家常做法,从擀皮到调馅全教你,汤鲜肉香,太好吃了
- 我家茄子从不炒着吃,淋入3个鸡蛋,比吃肉还香,上桌瞬间被扫光
- 引力波|从暗物质到暗物质引力波
- 有两种谷物被称为“长寿食材”,却很少人当主食吃,从今天要改
