很长一段时间,Web 开发的前景似乎没有什么进展(2016 年至 2021 年),但在刚刚过去的 2022 年中确实又猛窜了一波。今天主要想跟大家聊聊最新 Web 开发趋势。相信这波浪潮会继续激发 Web 开发者的关注,对万象更新的 2023 年更具期待。闲言少叙,马上进入正题。
一、(元)框架
单页应用程序(SPA)及各类相关框架(包括 React.js、Vue.js、Svelte.js 等)或多或少都经历过一定的炒作周期,也用多年阅历证明了自身强大的生命力。但随着以这些解决方案为基础的元框架的快速兴起,可以看到应用程序正在明显从客户端渲染(CSR)转向服务器端渲染(SSR)。如今,无论你使用哪一种 JavaScript 框架,总能看到 SSR 的影子。
其中最具人气的 Next.js 元框架就以 React.js 为基础。React 核心开发者 Andrew Clark 将 2022 年发布的新版本称为“真正的 React 18”,因为其中包含 React 团队为底层库基础构建块构建的所有 battery(例如 Suspense、流式 SSR 等)。Vercel(Next.js 背后的公司)也与 React.js 核心团队紧密合作,共同打造出色的开发者体验。
虽然不少开发者都对 Next.js 和 React.js 之间过于“亲密”的关系颇有微词,但 React.js 并非不可替代。最近刚刚被 Shopify 收购的 Remix,就采用不同方法将 React.js 转化为元框架(例如将 Web 标准设为优先)。而且在竞争之外,两套框架之间也有一定程度的功能融合(例如嵌套路由)。
除了现代 SSR 领域最有力的参与者、帮助众多前端开发者顺利成型为全栈开发者的 Next.js,其他一些重要框架同样值得大家关注:SvelteKit(基于 svelte.js 构建)及其最新 1.0 版本是由 Vercel 和 SolidStart(基于 Solid.js 构建)提供支持,较 React.js 拥有更好的开发者体验。
二、应用程序与渲染模式
虽然过去的十年(2010 年至 2020 年),Web 世界一直由单页应用程序(SPA)及其客户端渲染模式(CSR)所主导——从 Knockout.js 到 Ember.js,再到 Angular.js、React.js 以及 Vue.js 莫不如是——但最近两、三年,人们对使用元框架的服务器端渲染(SSR)越来越青眼有加。从外部来看,这似乎只是历史的又一轮循环,毕竟在多页应用程序(MPA)中使用 SSR 和 JavaScript(例如 jQuery、MooTools、Dojo.js 等)的作法早在 2005 年到 2010 年就曾盛极一时。但这波浪潮的意义绝不只是曾经的 Java(例如 JSP)或后来的 Ruby on Rails 被纳入 SSR,而在于 JavaScript 依赖性的不断增长。几年以来,Next.js 一直是这股变化背后的核心驱力,而 SvelteKit 等其他元框架也正在加入战团、共同促成这一历史性转变。
尽管两种模式的基本用途并不相同,但凭借长久以来与静态站点生成(SSG)的竞争,SSR 如今已经拥有近乎完美的性能表现(参考 Next.js 和 Gatsby.js)。在应用场景下,SSG 一般用于静态内容(例如博客等网站),而 SSR 则用于动态内容(例如 Web 应用程序)。如果需要考虑 SEO(搜索引擎优化),则 SSR 和 SSG 均适用。但如果需要提供高度动态的内容,或者是交付以用户为中心的内容并涉及身份验证,则 SSG 适用性较差(在部署前一次性构建,即静态);这时候最好是在 SSR(能根据服务器上的单个数据请求按需构建)或者是最近热度飙升的 CSR(在客户端上按需获取个别数据)间做选择。
但这里要强调,CSR、SSR 和 SSG 都不属于新兴的渲染技术。虽然 SSR 和 SSG 在前几年迎来了一波性能优化趋势,但实际发展的只是增量静态再生成(ISR)和流式 SSR 等更具体的渲染技术。前者改善了 SSG 性能,允许在每页基础之上静态重建整个网站。更进一步的方法还有按需 ISR,也称按需重新验证,可通过应用程序公开的 API 触发重建(例如在 CMS 数据更新时触发)。
另一方面,流式 SSR 则优化了服务器端渲染的单线程瓶颈。普通 SSR 需要在服务器上等待数据就绪,之后再将渲染完成的内容发送至客户端。相比之下,流式 SSR 允许开发者将应用程序拆分成多个块,让各个块逐步由服务器并行发送至客户端。
过去几年间,SPA/MPA 中的 SSG 和 SSR 渲染模式由极简单,逐步发展成如今愈发微妙的形态。而且不单是 ISR 和 SSR 流有所联系,部分水合(Partial Hydration,例如 React 服务器组件)允许仅在客户端上水合某些组件、渐进式水合可对水合顺序进行细粒度控制、Island 架构(例如 Astro)面向 MPA 中的隔离应用或组件,甚至出现了以可恢复性代表水合(例如 Qwik)的另一种有效方法。
三、边缘无服务器
SSR 和 SSG 等渲染技术与边缘无服务器的普及态势高度相关,原因是这些趋势均受到性能驱动,目的是在浏览器中提供无缝的用户体验。从本质上讲,正是为了向用户提供更快的网站和 Web 应用程序响应速度,才最终催生出边缘无服务器这一技术分支。
这里咱们还是从头开始慢慢捋顺:无服务器,又称无服务器函数、无服务器计算 (例如 AWS Lambda)或云函数(例如 Google.Firebase Cloud Functions),多年来一直在云计算领域占据一席之地。虽然无服务器并不是真正的不需要(远程)底层服务器,但开发者已经不必管理服务器及其相关任务(例如基础设施按需扩展)。相反,用户只需要将单一功能部署为无服务器函数,其他所有运维工作均由云服务商承担。
无服务器函数的出现带来了一大优势:由于不需要将应用程序服务器部署到特定一处或几处数据中心,我们首次实现了功能在世界各地的广泛覆盖。因此在理由情况下,无服务器函数能够尽可能贴近与用户间的距离,即最大程度降低客户端 - 服务器间的往返延迟,由此改善用户体验。也正是这种尽可能靠近用户部署无服务器函数的思路,创造出了边缘计算和边缘函数两个术语。
众多云服务商(包括 Cloudflare 和 Cloudflare Workers、Vercel 及其 Edge Network、Deno 及其 Deno Deploy 等)已经在这个领域展开竞争,各自努力为最终用户提供最佳交互时间(TTI)体验。边缘函数不仅能加快 SSG/SSR 内容的交付速度(因为连接最终用户的线路更短),而且能将结果缓存到离用户更近的位置。
但除了性能之外,边缘计算还在成本等其他重要因素上具备优势。例如,对于边缘函数,客户端与服务器之间往来的数据中有相当一部分并不需要交由主数据中心处理。在物联网场景中,有大量非相关数据(例如内容无任何变化的视频记录帧)其实没有任何意义,直接在边缘位置筛选即可。这就大大节约了数据传输与集中设施处理带来的日常开销。
四、数据库复兴
随着无服务器(边缘位置)的出现,数据库也迎来一波复兴。使用无服务器函数,开发者很快就会遇到数据库连接开启过多的问题,这是因为新的边缘设施形态导致每台服务器不再固定保持一条开启连接,而是每个无服务器函数都与数据库一一连接。虽然连接池能够很好解决问题,但用户要么需要自建,要么由第三方服务商提供。
无服务器数据库领域的热门竞争者包括 PlanetScale(MySQL)、Neon(PostgreSQL)和 Xata(PostgreSQL),它们具备数据库分支、schema diffing 和强大的搜索 / 分析 / 洞察功能。遍布全球各地的无服务器设施只需要提供边缘缓存或分布式只读数据库,确保让数据尽可能靠近用户位置最大程度降低延迟。
如果第三方服务不仅需要分发数据库,还需要分发应用程序,Fly.io 能够将所有内容打包至单一平台当中。这类应用就超越了常规数据库,进而推动新的技术变革。人们常将 Railway 视为 Heroku 的继任者,它为平台即服务(PaaS)带来了部署技术堆栈所需要的一切。如果大家希望将服务链上移至后端即服务(BaaS),则可通过 Supabase 使用 Firebase 的开源替代方案,获得应用程序 / 数据库托管、身份验证和边缘函数等功能。
五、JavaScript 运行时
一切都始于 Ryan Dahl 在 2009 年一场会议上公布的 Node.js。最初,Node.js 的目标只是将 JavaScript 和浏览器拆分开来,尝试将其运行在服务器端。但后来,JavaScript 成为过去十年间最成功的 Web 开发驱动力。本质上,Ryan Dahl 在无需浏览器本体的情况下,为 Node.js 开发出了名为 V8 的 JavaScript 引擎(由 Chrome 实现)。因此,Chrome 浏览器和 Node.js 使用的是完全相同的 JavaScript 引擎,但二者各自有自己的 JavaScript 运行时(例如浏览器 API 与节点 API)来实现交互。
十年之后,Ryan Dahl 宣布 Deno 成为 Node 的继任者,并承诺为开发人员提供一个更安全、更快捷的环境,其中还将包括浏览器 API、TypeScript 和一个开箱即用的标准库。Deno 同样运行在 V8 引擎之上,但如今的它只是众多 JavaScript 运行时中的一种。
在边缘函数这一竞争领域,各云服务商也在纷纷实现自己的 JavaScript 运行时(例如 Cloudflare Workers,专门针对自家 Cloudflare 基础设施进行了优化)。因此,Deno 的商业模式也开始向云服务商转型,打造出 Deno Deploy 及其即时边缘渲染 SSR 框架(最初仅为概念验证)Deno Fresh。此外,像 Bun(以运行在 JavaScriptCore 引擎上,却依托于 Zig 实现而闻名)这样的独立解决方案,也在这场以速度为比拼要素的 JavaScript 运行时竞赛中获得了一定关注。
面对这么多运行时选项,相信敏锐的读者朋友肯定感受到了技术碎片化的倾向。如果协调不当,那我们又会像当年各种各样的浏览器那样疲于为 JavaScript 提供支持。但好在这次竞争的焦点在于服务器端,而且不同云服务商对于各种 JavaScript 运行时的关注度也大有区别。为了保持江湖地位,Deno、Vercel、Cloudflare 等利益相关方纷纷加入 WinterCG,表示愿意就 JavaScript 运行时间的 API 互操作性开展合作。
六、Monorepo
过去,Monorepo 策略主要用于大型应用程序,其中各项目在单一版本控制仓库中仅包含较小体量。这些较小的项目单元可能是独立应用程序(例如 SPA、MPA),也可能是可复用包(例如函数、组件、服务等)。这种项目拆分再合并的作法可以追溯到 2000 年初,那时候的名称叫共享代码库。
但如今的 Monorepos 不仅面向大型应用程序,同时也开始服务于小型企业和开源项目。例如,一家公司可以在 Monorepos 中包含各种包,例如共享 UI 组件、共享设计系统(例如可复用的协作设计)以及不同领域的日常实用工具函数。
这些包可以在各种应用程序中直接导入:使用所有共享包的实际应用程序(例如 app.mywebsite.com 客户端渲染)、仅使用共享设计系统包且考虑 SEO 需求的主页 / 产品 / 登陆页面(例如由服务器端渲染或静态站点生成的 mywebsite.com),以及使用共享 UI 组件和共享设计系统包的技术文档页面(例如 docs.mywebsite.com)。
现已被 Vercel 收购的 Turborepo,目前就致力于在 JavaScript/TypeScript 中大肆宣传 Monorepo 方法。Turborepo 帮助开发团队在 Monorepo 中为所有应用程序和包创建构建管线。其最大亮点,就是能在本地机器或云端实现跨团队的管线内 build 缓存。
Turborepo 与 npm/yarn/pnpm 工作区(依赖项管理)和变更集(版本控制)等其他重要 Monorepo 工具相结合,共同为这部分开发生态吸引到了全球 Web 社区的目光。
Turborepo 的竞争对手包括 Nx、Rush 和 Lerna(一段时间停止维护,后被 Nx 开发商 Nrwl 所收购)。
七、实用工具优先的 CSS
对这波趋势,喜欢的超喜欢、讨厌的特讨厌。Tailwind CSS 是实用工具优先 CSS 的典型代表。一方面,开发人员讨厌它的存在令 UI 代码显得冗长;但另一方面,开发者又喜欢它出色的开发体验。作为直接受众,开发人员只需要在项目中进行一次配置,即可立即在 HTML 中使用其预定义的 CSS。
但随着近期服务器端渲染(SSR)的兴起,这种关于实用工具优先 CSS 的爱恨割裂有望彻底结束。几年来,像 Styled Components(SC)和 Emotion 这样的 CSS-in-JS 解决方案,一直是现代基于组件的 Web 应用程序样式的主导力量。然而,如果说 SSR 世界始终以性能为至高目标,那 CSS-in-JS 的存在本身就是反性能的:它会让包更加臃肿(SC 为 12.7 kB,Emotion 为 7.9 kB),而且在插入 DOM 前的 CSS 序列化也会带来额外的运行时开销。
因此,我们可能会看到开发人员转向对 SSR 更友好的解决方案,例如将实用工具优先 CSS(例如 Tailwind CSS、UnoCSS)与预定义的 UI 组件(例如 DaisyUI)配对,使用 CSS 模块等其他同样流行的替代方案,或者选择零运行时 / 编译时 CSS-in-JS 类方案(例如 vanilla-extract、linaria、astroturf、complied 等)。
八、配合 TypeScript 实现端到端类型安全
从 JavaScript 到 TypeScript 的演变已经势不可挡。在这场席卷整个 Web 开发世界的大迁移中,全栈应用的端到端类型安全无疑是一大核心驱力。这个概念的实现与通信层(API)密切相关,因为通信层需要将类型化的实体(例如 type User、type BlogPost 等)从服务器桥接至客户端应用程序。
在涉及客户端 -0 服务器通信的 Web 开发中,常见的选项是 REST 和 GraphQL。二者能与 OpenAPI for REST 和 GraphQL Code Generator for GraphQL 配合使用,为前端应用程序生成类型化的 schema 文件。
除此之外,还有名为 tRPC 的类型安全 API 后起之秀,它已经证明自己完全有能力成为 REST/GraphQL 的替代方案。如果您已经使用了前端和后端共享代码的 TypeScript Monorepo,tRPC 允许大家将所有类型从后端导出至前端应用程序,过程中无需生成任何类型化 schema。之后,前端只须使用在后台通过 HTTP 连接的类型化函数即可调用后端 API,实现客户端 - 服务器间通信。未来,全栈应用程序的总体趋势一定会转向这种类型安全解决方案。作为其中的典型代表,tRPC、Zod、PrismatTanStack Router 都能在应用程序边缘提供类型安全保障。
九、构建工具
在 React-land 中,create-react-app(CRA)曾多年占据主导。这在当时掀起了一场小小的革命,因为初学者获得了一个随时可用的 React 入门项目,不再需要使用 React 配置自定义 Webpack。但过去短短一年之间,Webpack 却迅速过时
Vite 虽然是单页应用程序(SPA)领域的新秀,但却能跟所有流行框架(例如 React.js)配合构建入门项目。作为 Vue.js 缔造者尤雨溪的又一力作,Vite 的定位是下一代前端工具。在引擎盖之下,它从 esbuild 处继承了强大的功能;而且跟其他 JavaScript 打包器相比,它是用 GO 编写的,因此打包依赖项的速度能达到竞争对手(例如 Webpack)的 10 到 100 倍。
Vite 的生态系统是伴随着 Vitest(Jest 的测试替代方案)等新增功能而蓬勃发展,同时 Vercel 的 Turbopack 等同类竞争方案近期也开始涌现。Turbopack 被称为 Webpack 的继任者,因为它是由 Webpack 的缔造者 Tobias Koppers 牵头开发完成。由于 Next.js 既是 Webpack 的现用户,一边又是 Turbopack 的开发商,所以预计 Next.js 和 Turbopack 在后续将成为紧密关联的一对 Web 组合。
十、AI 驱动开发
AI 最终会消灭开发者的工作岗位吗?这个问题还贿答案,但 AI 驱动开发确实在 2022 年内成为了现实。随着 GitHub Copilot 的发布,开发者们能够在自己喜爱的各种 IDE 中与 AI 助手结对。其使用过程与常规编码或者注释编写没什么区别,GitHub Copilot 会自动补全细节以尽量提升代码质量。
还不止于此:OpenAI 的 ChatGPT 是一套高度通用的语言模型,而且在编程领域也有不俗表现。没错,ChatGPT 既能回答形式多样的自由提问,也能生成颇为靠谱的开发成果。不少开发者不知不觉减少了对 Stack Overflow 的访问,转而跟 ChatGPT 讨论技术问题。在多数情况下,ChatGPT 都能以搜索引擎替代品的姿态提供非常有用的答案(虽然还称不上完美)。相较于存在大量 SEO 垃圾、甚至跟开发毫无关联的广告内容,ChatGPT 的使用感受相较于传统搜索引擎提升了一大截。
但请注意,这种短期收益也许会带来深远的危害。宏观来讲,AI 创建的内容可能、甚至可以说一定会危害整个互联网。以往手动创建的 SEO 宣传内容已经是个大难题,未来没人拦得住 ChatGPT 以人类无法比拟的效率自动生成更多 SEO 垃圾。如果 ChatGPT 自己在训练中也继续使用这些垃圾内容,后果将不堪设想。
还有一些很重要,但未被列入十大的重要趋势。首先,Tauri 作为 Electron 的替代品开始进入 JavaScript/CSS/HTML 实现的桌面应用程序;Playwright 正成为 Cypress 的 E2E 测试替代品;Warp 与 Fig 有望成为下一代终端;CSS 容器查询则作为 CSS 媒体查询的响应式设计替代方案;最后,htmx 作为富 HTML 格式,能够不借助 JavaScript 创建出交互式用户界面。
希望这篇文章能帮大家更好地了解 Web 开发生态系统的发展现状。Web 开发无界,江湖有缘再见!