写点什么

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:022547

评论

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

运输车辆超时停车预警难?TDengine 流式计算助力吉科软轻松解决

爱倒腾的程序员

数据库

华为云云原生数据库,让企业离应用更进一步

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 7 月 PK 榜

Seal AppManager如何基于Terraform简化基础设施管理

SEAL安全

Kubernetes Terraform 平台工程 SealAppManager 企业号 7 月 PK 榜

如何学习ABAQUS有限元仿真分析软件

思茂信息

abaqus abaqus软件 abaqus有限元仿真 有限元分析 有限元仿真

Leangoo领歌敏捷工具提供哪些Scrum敏捷培训?

顿顿顿

敏捷开发 敏捷开发管理工具 scrum工具 scrum培训 敏捷培训

手机直播app源码部署搭建:带货潮流,商城功能!——山东布谷科技创作

山东布谷科技

软件开发 直播 源码搭建 直播APP源码 手机直播源码

任务调度之时间轮实现 | 京东云技术团队

京东科技开发者

定时任务 数据结构与算法 时间轮 企业号 7 月 PK 榜

虚拟平台中的“有意”/“无意”故障注入

DevOps和数字孪生

故障注入 虚拟平台

WorkPlus AI助理:结合ChatGPT对话能力与企业数据,助力企业级AI构建!

BeeWorks

带你掌握利用Terraform不同数据源扩展应用场景

华为云开发者联盟

开发 华为云 华为云开发者联盟 企业号 7 月 PK 榜

书画家点赞!基于飞桨绘制中国水墨山水画

飞桨PaddlePaddle

人工智能 paddle 飞桨 百度飞桨 AIGC

含CPU芯片的PCB可制造性设计问题详解

华秋PCB

工具 芯片 PCB PCB设计 可制造性

基于ClickHouse解决活动海量数据问题 | 京东云技术团队

京东科技开发者

数据库 Clickhouse 数据处理 企业号 7 月 PK 榜

从TL、ITL到TTL | 京东物流技术团队

京东科技开发者

ThreadLocal ThreadLocalMap 企业号 7 月 PK 榜

NFTScan | 07.10~07.16 NFT 市场热点汇总

NFT Research

NFT\

火山引擎A/B测试“广告投放实验”基础能力重构实践 (DataFunTalk渠道)

字节跳动数据平台

大语言模型评估全解:评估流程、评估方法及常见问题

Baihai IDP

人工智能 白海科技 LLMOps 大模型评估 企业号 7 月 PK 榜

龙蜥社区首次突破!高性能存储 SIG 现身 LSF/MM/BPF 2023 分享 EROFS 的演进路线

OpenAnolis小助手

开源 高性能存储 龙蜥社区 sig EROFS

火山引擎DataLeap如何解决SLA治理难题(一):应用场景与核心概念介绍

字节跳动数据平台

数据中台 数据治理 SLA 数据研发 企业号 7 月 PK 榜

常用语言的线程模型(Java、go、C++、python3) | 京东云技术团队

京东科技开发者

Java c++ Go 线程模型 企业号 7 月 PK 榜

等保定级标准是怎样的?是否系统定级越低越好?

行云管家

网络安全 等保 堡垒机 等级

关于 Elasticsearch 不同分片设置的压测报告

极限实验室

索引 压测 ES

私有化的即时通讯软件能给企业带来什么好处?

BeeWorks

IPD(集成产品开发)跟敏捷、DevOps一样吗?有什么区别?

禅道项目管理

DevOps 敏捷开发 IPD

七年老程序员的五六月总结:十一件有意义的事

拭心

android 程序员 程序人生 成长记录

如何使用openEuler WSL sideload

openEuler

Linux 前端 操作系统 wsl openEuler

Nautilus Chain NautDID NFT 将上主网,Layer3 数字身份时代开启

西柚子

WiFi 7 (802.11be)-IPQ9574+QCN9274-ultra-wide 320 MHz spectrum channel

wifi6-yiyi

qcn9274 WiFi 7 802.11be

应对618、双十一等大促期间的高负载,API性能测试应该怎么做?负载测试、基线测试、冒烟测试、浸泡测试、峰值测试和尖峰测试详解

龙智—DevSecOps解决方案

自动化测试 API 测试自动化

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