【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

React 从入门到放弃,一个关于网页速度的故事

  • 2020-09-16
  • 本文字数:3261 字

    阅读完需:约 11 分钟

React从入门到放弃,一个关于网页速度的故事

2011 年的时候,我碰巧找到一份编写Backbone.jsapp 的工作。如果你从没做过那样的工作,千万别去做。我会给那些愿意听我抱怨的人,喋喋不休地讲我遇到的各种困难。当我开始深入研究前端的替代方案时,我发现了FRPFlapjaxClojureScript。其中 ClojureScript 让我迷上了Clojure。我甚至针对 FRP 和 ClojureScript (以及Hoplon的前身 hlisp)做了一场成功的演讲

React

然后在 2013 年的时候,React 发布了。我在我的新工作中尝试了 React,并在 Clojure 主题的峰会(Clojure Cup 2013)期间发现 CLJS 和 React 简直是天作之合。React 为什么这么好呢?对于我来说,主要的卖点是它组件化做的非常好。


当你使用之前的东西,例如 jQuery、Backbone、Angular 或者其它东西,只需一年的开发之后,你的代码就会是一团事件监听器和触发器。千万别让我碰那些莫名其妙的 JS,jQuery 根本就不存在代码封装。哪个处理器在哪里绑定,用来做什么?很难说这是一个好的基础库!


然后我开始在Kasta工作,那里的 web 前端完全就是一团 jQuery 式的玩意儿。没人想要碰那些代码,因为你会花费数小时,甚至数天,来做一些最小的改动。然后 QA 会发现比你想象的多得多的错误状态。然后用户会向我们的呼叫中心报告更多的错误。那糟糕程度简直超乎你的想象。


因此,在进行了一些实验、测试和检查后,我决定使用 React + ClojureScript 技术栈,用 Clojure 进行服务器端渲染。

没落

有一段时间,一切看起来都很好。我们的有这样的架构:我们的组件在后端作为 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(响应时间)在这里变得更加重要。

变革时刻

既然情况已经改变,那我们要怎么做呢?在 React 出现之前,我读过“我如何靠 vanilla JS 生存”这类的文章,但这些文章通常没有任何意义——它要么是一个关于它有多伟大的喧嚣,而无视所有问题(关注点分散、内聚性、组件化、代码封装),要么是一个(或几个)人在脑袋里想想而没有实际代码的项目。


早在二月的某个时候,我偶然发现了Intercooler.js。我不确定我以前是否见过它——也许我看过但一瞥而过——但这没关系。这一次,它引起了我的注意。


其理念是,所有的 HTML 都在服务器端渲染。而客户端根据元素的属性,更新部分 HTML。基本上类似 HTML+XHR。你不能任意妄为,但这是其重点之一:有些限制是好的,从而让你不会做一些疯狂的事情。你需要服务器的一些支持,这样你就可以渲染部分结果——虽然只是一个优化,但确实非常重要。


还有一个备选库——Unpoly。这个库围绕布局和样式有更多特性,但是在 XHR 上的想法比较少(例如,如果没有表单,很难完成带参数的 POST 请求)。而且这个库大的多。而且它是用 CoffeeScript 编写的,有许多类,这也有点问题


所以我用 Intercooler 对我们的目录页做了一个概念验证式的实现,而且它成功了!除了对 jQuery 有依赖和其它一些令人恼火的东西外…当我纠结于对 HTML 片段的请求时,我明白了一件事:当我为目录页选择技术路线图时,最后的选择是“类似 intercooler 的小东西”。


那为什么还不行动呢?

TwinSpark

我喜欢 Intercooler 在处理 AJAX 方面的流畅方案,所以我决定用一些汽车方面的东西来命名这个库,而 TwinSpark 似乎是一个不错的名字。这具体是个什么东西呢?


TwinSpark是一个用于声明式 HTML 增强的框架:你在元素上添加额外的属性,TwinSpark 对它们对一些处理。例如发起一个 AJAX 调用并用响应替换目标,或者增加一个样式类,或者,你们自己看看这些例子吧。


当然,它与 Intercooler 有些不同之处,不然它为什么会存在?最值得一提的就是,它不依赖 jQuery。它只支持现代浏览器(不支持 IE 和 Opera Mini)而抛弃掉了 88kb 的兼容性怪代码。


它还有以下优点:


  • 没有继承——这点再强调也不过分!

  • 指令有明确的扩展点

  • 支持对服务器的批量请求

  • 更严格的属性命名规范(这是我的观点,但ic-getic-post使我烦恼:别让我改变观点啊!)

  • 负载更小(多亏了没有 jQuery!)

  • 应该更快(也多亏了没有 jQuery)


老实说,最主要的原因是批处理无继承性。继承在这里尤其痛苦。在 Intercooler 中,如果你在 body 中声明ic-target属性,其中的所有标签都会认为它们的 target 也是这个。这样的话,在 HTML 树的某个地方有一个组件,而树上更高位置的一个属性改变了这个组件的行为。我认为这是一个奇怪的动态范围,我可不想要那样!:)


有趣的是,在对 TwinSpark 进行了一个月的尝试之后,Intercooler 的作者宣布它在做一个没有 jQuery 的现代版本:htmx。它有非常好的扩展点,而且可能会增加批处理…但是仍然有继承。:-(

TwinSpark 为什么是一个好主意

我们需要从两个方面看待它:它是否对开发者友好以及它是否对用户友好。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 测试,而无需编写二次标签。

结论

从首次尝试到发布,我们花了 4 个月时间。并不是我们刚开始时预想所需的时间(“应该最多需要两三周!”),呵呵,但并不是只有我们这么做。从代码中移除 React 相关代码并将我们的 app 打造成一个服务端应用程序仍然花费了很多时间和精力。它仍然需要一些润色,但我们还是决定发布它来缩短时间。A/B 测试显示我们是对的——特别是对于 Android 手机。


谷歌现在给我们的目录页排名 75/100 而不是 5/100。我想,这还是很不错的吧?:)


作者介绍:


Alexander Solovyov 现在是Kasta公司的首席产品官,之前是那里的 CTO。我们致力于为消费者和生厂商构建一站式电子商务平台。


原文链接:


A tale of webpage speed, or throwing away React


2020-09-16 10:022400

评论

发布
暂无评论
发现更多内容

深入浅出总结Flink运行时架构

百思不得小赵

大数据 flink 6月月更

在线SQL转HTMLTable工具

入门小站

工具

Java中java.util.Arrays参考指南

okokabcd

Java

数据生态第三弹 | RocketMQ OpenMLDB Connector,实时数据到特征工程的高速传输

第四范式开发者社区

人工智能 云原生 实时数据 特征平台 特征工程

最新版CorelDRAW Technical Suite2022

茶色酒

cdr2022

在线文本过滤小于指定长度工具

入门小站

工具

融云通信解决方案 破解企业沟通痛点

融云 RongCloud

笔记

IT蜗壳-Tango

6月月更

高效远程办公的基石:有效沟通 |社区征文

wljslmz

远程办公 初夏征文

00 后云原生工程师:用开源 Zadig 为思创科技(广州公交)研发开源节流

Zadig

DevOps 研发效能 工程师 自动化运维

要想Linux命令行玩的溜,还得apropos!此文运维必看!

wljslmz

Linux 运维 6月月更

油猴脚本学习

Sher10ck

脚本 油猴

悬赏平台并没有WEB端开发,在原生开发和混合开发中哪种合适?

开源直播系统源码

软件开发 app源码 原生开发 混合开发 悬赏平台源码

linux检测系统是否被入侵(下)

入门小站

Linux

Prometheus 2.36.0 新特性

耳东@Erdong

release Prometheus 6月月更

自媒体行业内卷严重:企业自媒体应该何去何从

石头IT视角

Go Web 编程入门:HTTP 自定义路由

宇宙之一粟

Go 语言 6月月更

Android包体积优化(常规、进阶、极致)

yechaoa

android 性能优化 包大小 6月月更 包体积优化

这玩意叫跳表?

慕枫技术笔记

数据结构 算法 6月月更

架构实战营|模块5

KDA

#架构实战营

8年打磨,《游戏设计梦工厂》发布史诗级更新!

博文视点Broadview

带链接跳转的微信红包封面制作教程和使用指南

boshi

小程序 微信红包封面 微信红包

HashMap分析-基础属性与结构

zarmnosaj

6月月更

rxjs Observable 设计原理背后的 Pull 和 Push 思路

Jerry Wang

typescript 响应式编程 angular RXJS 6月月更

微博评论架构设计

泋清

#架构训练营

稳!上千微服务如何快速接入 Zadig(Helm Chart 篇)

Zadig

DevOps 微服务架构 持续交付 自动化运维 Zadig

中科方德技术专家直播:如何基于 OpenStack、Ceph 构建私有云平台? | 第 27 期

OpenAnolis小助手

Ceph 龙蜥大讲堂 中科方德 OpenStack 私有云平台

设计电商秒杀系统

凯博无线

CTO专访:合见工软深化产品布局 加速国产EDA技术革新

科技热闻

Ubuntu环境下载OpenJDK11源码

程序员欣宸

Java Openjdk 6月月更

【Go实现】实践GoF的23种设计模式:装饰者模式

元闰子

Go 设计模式 装饰器 装饰者模式

React从入门到放弃,一个关于网页速度的故事_架构_Alexander Solovyov_InfoQ精选文章