在这次直接对决中,Gatsby 战胜了 Next.js。
我用 Gatsby 和 Next.js 开发了同一个 Web 应用,发现 Gatsby 的性能更好。
由于新冠疫情持续蔓延和社交隔离措施的影响,许多活动被迫转为线上虚拟活动。我是Antler的一名软件工程师,Antler 运营着一个全球创业孵化项目,通常每年举办多场线下 Demo Day 活动,展示大约十几家新创公司,我们也面临着同样的情况。
我们希望打造流畅的线上体验,让用户专注于内容——我们投资组合公司的路演。考虑到本次活动的受众范围较广,而且这可能是用户首次接触 Antler 的线上平台,我们需要做到尽善尽美,确保页面加载速度快。这正是高性能渐进式 Web 应用 (PWA) 的绝佳应用场景。
太长不看
-
在数据加载期间显示框架图,比服务器加载数据时只显示空白页面,能让应用程序看起来更快。
-
Gatsby 的静态输出速度只比 Next.js略快一些,但 Gatsby 的插件和文档提供了更好的开发体验。
服务器端渲染还是静态网站生成?
简单介绍一下背景:我们所有的 Web 产品都是用 React 和 Material-UI 库构建的,所以我们沿用了这套技术栈,以保持开发速度并确保新代码与其他项目兼容。关键区别在于,我们所有其他的 React 应用都是使用 create-react-app 启动的,并且完全在客户端渲染 (CSR),因此在初始 JavaScript 代码解析和执行期间,用户会看到一个空白的白色屏幕。
因为我们想要获得一流的性能,所以我们正在研究利用服务器端渲染 (SSR) 或静态站点生成 (SSG) 来改善初始加载体验。
我们将通过 Algolia 从 Cloud Firestore 获取数据,以便使用受限 API 密钥对公共数据访问进行更精细的字段级控制。这也有助于提升查询性能:据我们了解,Algolia 的查询速度更快,而且 Firestore JavaScript SDK 的gzip 压缩后大小为 86 KB,而 Algolia 的 SDK 压缩后仅为7.5 KB。
我们也希望确保提供的数据尽可能新鲜,以防任何错误被实时发布。虽然标准的静态站点生成器 (SSG) 做法是在编译时执行这些数据查询,但我们预计管理界面、Firetable和创始人门户网站都会频繁地向数据库写入数据,导致多个构建同时运行。此外,我们的数据库结构可能会导致无关的更新触发新的构建,使我们的 CI/CD 流水线效率极低,因此我们需要在用户请求页面时查询数据。遗憾的是,这意味着它不能是一个“纯粹的”SSG Web 应用程序。
最初,我们使用 Gatsby 构建了这款应用,因为我们之前已经维护过一些用 Gatsby 构建的落地页,其中一个落地页还使用了 Material-UI。这个初始版本生成的页面在数据加载时会先显示一个框架,并且首次内容绘制时间大约为 1 秒。🎉
但由于数据是在客户端加载的:
-
用户需要在页面初始加载后等待一段时间才能查看实际内容,并等待 Algolia 完成四个网络请求。
-
由于 React 需要将框架替换为内容,浏览器 JavaScript 引擎的工作量更大了。这会增加 DOM 操作!
-
搜索引擎爬虫可能无法加载内容,而且它们通常更喜欢静态网站。
所以,趁着一个公共假期长周末,我决定尝试一下使用 Next.js 实现服务器端渲染版本。幸运的是,Material-UI 已经提供了一个Next.js 的示例项目,所以我不用从头开始学习这个框架——我只需要浏览一下教程和文档中的特定部分即可。转换应用并在每次请求时从服务器端查询数据,解决了上面提到的所有三个问题,最终结果是……
首次有效绘制时间大约增加了两倍。
此外,Lighthouse 速度指数增加了四倍,首字节到达时间从 10-20 毫秒增加到 2.56 秒。
值得注意的是,Next.js 版本托管在不同的服务上(ZEIT Now 与 Firebase Hosting 相比——这可能也是导致 TTFB 较高的原因之一),但很明显,将数据获取步骤推到服务器会产生看似更慢的结果,即使内容加载的时间大致相同,因为用户只能看到一个空白的白色页面。
这凸显了前端开发中的一个重要教训:为用户提供视觉反馈。一项研究发现,使用骨架屏的应用程序加载速度更快。
如果你过去几年一直在阅读有关网页开发的文章,你可能会注意到,这一结果与一种观点相悖:
客户方并非邪恶的一方。
SSR 并不是解决性能问题的万能方案。
Gatsby 与 Next.js:静态网站生成性能对比
虽然这两个框架分别以静态网站生成和服务器端渲染应用程序而闻名,但Next.js 9.3 对其 SSR 实现进行了彻底改造,以与 Gatsby 相媲美。
撰写本文时,此次更新发布仅一个多月,仍位于 Next.js 的主页上,而且几乎没有关于这两个框架的静态站点生成 (SSG) 实现方式的比较。因此,我决定亲自进行一项实验。
我将 Gatsby 版本所做的更改还原为客户端数据获取,并确保两个版本具有完全相同的功能集:我禁用了 SEO 功能、网站图标生成和 PWA 清单,这些功能由 Gatsby 插件处理。为了仅比较框架生成的 JavaScript 包,没有加载任何来自外部源的图片或其他内容,并且两个版本都部署在 Firebase Hosting 上。作为参考,这两个版本均基于 Gatsby 2.20.9 和 Next.js 9.3.4 构建。
我在本地机器上对每个版本都运行了六次 Lighthouse 测试。
结果略微偏向 Gatsby:
Next.js 版本在整体性能得分、首次内容绘制时间和速度指数方面略逊于 Gatsby。它的最大潜在首次输入延迟也更高。
为了找到答案,我深入 Chrome 开发者工具的网络面板,发现 Next.js 版本将 JavaScript 有效负载拆分成了三个额外的块(忽略生成的清单文件),但压缩后的有效负载却小了 20 KB。这些额外的请求是否会抵消更小包大小带来的优势,从而影响性能呢?
通过对 JavaScript 性能的分析,开发者工具显示 Next.js 版本完成首次绘制的时间比 JavaScript 版本长 300 毫秒,并且运行时脚本的评估也耗时过长。开发者工具甚至将其标记为“耗时任务”。
我对比了项目的两个分支,看看是否存在任何实现上的差异导致了性能下降。除了移除未使用的代码和修复缺失的 TypeScript 类型之外,唯一的改动是实现了在页面特定区域导航时的平滑滚动效果。这部分代码之前位于一个gatsby-browser.js文件中,现在被移到了一个动态导入的组件中,这样它就只会在浏览器中运行。(我们使用的 npm 包smooth-scroll需要window在导入时提供该对象。)这很可能就是罪魁祸首,但我不太熟悉 Next.js 是如何处理这个特性的。
Gatsby 拥有卓越的开发者体验
最终,我决定继续使用 Gatsby 版本。暂且不谈它相比 SSG Next.js 那微乎其微的性能优势(我难道真的要纠结于 0.6 秒的差距吗?),Gatsby 版本已经实现了更多 PWA 功能,重新实现这些功能实在不值得。
最初构建 Gatsby 版本时,我很快就完成了最后的润色,打造了更完善的 PWA 体验。要实现页面特定的 SEO 元标签,我只需阅读他们的指南。要添加 PWA 清单文件,我只需使用他们的插件。至于如何正确实现支持所有不同平台的网站图标(favicon),这至今仍然是个棘手的问题,不过,这已经包含在我刚刚安装的清单插件中了。太棒了!
在 Next.js 版本中实现这些功能需要花费更多时间搜索教程和最佳实践,而且并不会带来任何好处,尤其是在 Next.js 版本本身并没有提升性能的情况下。这也是我在与 Gatsby 版本进行比较时决定禁用这些功能的原因。虽然 Next.js 的文档更加简洁(可能是因为它比Gatsby更精简),而且我非常喜欢他们游戏化的教程页面,但 Gatsby 更全面的文档和指南在实际构建 PWA 时更有价值,即使它乍一看可能让人不知所措。
不过,Next.js 也有很多值得称道的地方:
-
由于配备了教程和更简洁的文档,它的学习曲线感觉更平缓。
-
它的主要数据获取架构围绕
async函数展开fetch,因此您不必学习 GraphQL 即可充分利用该框架。 -
它开箱即用地支持 TypeScript,而 Gatsby 需要单独的插件,甚至连类型检查都不做——类型检查也需要单独的插件。(将应用迁移到 Next.js 时,这导致了一些问题,因为我根本没意识到类型定义错误,导致编译失败。)
Next.js 经过全面改进,对 SSG 的支持使其成为一个强大的框架,可以轻松地逐页选择 SSR、SSG 和 CSR。
事实上,如果我能够完全静态地生成这个应用,Next.js 会更合适,因为我可以使用 Algolia 的默认 JavaScript API,并将数据获取代码与组件放在同一个文件中。由于 Algolia 没有内置的 GraphQL API,也没有适用于 Algolia 的 Gatsby 源码插件,因此在 Gatsby 中实现此功能需要将代码添加到新文件中,这与更直观的声明式页面指定方式相悖。
总有更多性能提升的空间。
解决了这个问题之后,还有更多的性能改进空间,以便更接近 Lighthouse 的 100 分性能评分。
-
Algolia 在2020 年 3 月的简报中建议添加一个
preconnect提示以进一步提高查询性能。(遗憾的是,邮件中的代码片段有误;以下是正确的代码片段。) -
静态文件应该永久缓存。这包括 Gatsby 的 webpack 配置生成的 JS 和 CSS 文件。Gatsby对此有非常完善的文档,甚至还提供了插件来为 Netlify 和 Amazon S3 生成这些文件。遗憾的是,对于 Firebase Hosting,我们需要自己编写相应的实现。
-
我们之前提供的图片都是创始人上传的 JPEG 或 PNG 格式,未经压缩或优化。改进这一点需要更复杂的工作,超出了本项目范围。此外,如果能将所有这些图片转换为 WebP 格式,并仅存储一种高效的图片格式,那就太好了。遗憾的是,与许多 PWA 功能一样,Safari WebKit 团队在这方面进展缓慢,目前它是唯一一款不支持 WebP 的主流浏览器。
感谢阅读!通常我会发布最终项目的链接,但由于法律原因,无法公开分享。
您可以在 Twitter 上关注我@nots_dney,获取最新消息,我将撰写并分享更多关于我作为前端工程师的经验。
文章来源:https://dev.to/notsidney/gatsby-won-against-next-js-in-this-head-to-head-37ka




