- 前端
- 静态站点
- Web框架
【免费下载链接】gatsby
React-based framework with performance, scalability, and security built in.
Gatsby v3.10.0(2021 年 7 月第二个版本)的核心主题是为构建链路中最耗时的环节注入并行能力与缓存能力:一方面通过PARALLEL_QUERY_RUNNING实验特性把原先只能在单进程内串行执行的查询运行(query running)分散到多个子进程中,充分利用多核 CPU 与内存;另一方面通过DEV_WEBPACK_CACHE实验特性把 webpack 5 的文件系统持久缓存引入gatsby develop,显著缩短开发服务器启动时间。读完本文,你将掌握这两个实验特性的开启方式、底层实现原理(gatsby-worker+ LMDB)、官方基准测试方法,以及它们在后续 Gatsby 版本中的演进去向。
一、发布背景:v3.10 的两个关键词
Gatsby 的构建过程由多个步骤串联而成(详见 Gatsby 构建流程概览),其中有一个步骤会随着页面(pages)和数据节点(nodes)数量的增长而不断变慢,这就是查询运行。在构建日志中,你通常能看到两个进度条目:
run static queries(静态查询)run page queries(页面查询)
v3.10 之前的查询运行步骤只在单一进程中执行,无法利用现代服务器普遍具备的多核 CPU 与富余内存。v3.10 推出的实验特性 Parallel Query Running(PQR,并行查询运行)目标就是把查询工作分发到多个进程中,从而更好地利用可用核心与内存。
与此同时,Gatsby 在 v3.8 中已经为生产构建(production build)启用了 webpack 5 的持久缓存(见 v3.8 发布说明),v3.10 则开始把同样的能力逐步推广到gatsby develop,以大幅缩短开发服务器启动时间。
Bleeding Edge 提示:如果你希望第一时间体验新特性,可以安装
gatsby@next版本,并在遇到问题时向官方提交 issue。上一版本见 v3.9 发布说明。
二、实验特性一:Parallel Query Running(PARALLEL_QUERY_RUNNING)
2.1 为什么查询运行是构建瓶颈
在 Gatsby 的构建过程中,查询运行阶段需要为每一个页面执行 GraphQL 查询,并写入对应的page-data.json。页面越多、节点越多,这一步耗时越长。由于它此前只能在单进程内顺序执行,即便机器有 16 核,查询工作也只会吃掉一个核的算力。这正是 PQR 想要解决的痛点。
2.2 底层实现:gatsby-worker+ LMDB
PQR 的实现依赖两个关键基础设施:
gatsby-worker:Gatsby 自研的子进程任务调度工具,其设计高度借鉴了社区广受欢迎的jest-worker。仓库中 gatsby-worker 源码 清楚地展示了它的工作方式:- 基于 Node.js
child_process的fork方法派生子进程,每个 worker 进程通过 IPC 与主进程通信; - 提供
WorkerPool类,将 worker 模块中导出的函数自动暴露为single(调度到某一个 worker 执行,用于任务分发)和all(在所有 worker 上执行,用于初始化等场景)两类方法; - 内置
TaskQueue任务队列,空闲 worker 会自动从队列中领取下一个任务(见 index.ts 中的checkForWork逻辑); - 每个 worker 会继承主进程环境变量,并获得额外的
GATSBY_WORKER_ID标识;numWorkers默认值为 1,未指定时只派生一个 worker(见 gatsby-worker README)。
- 基于 Node.js
- LMDB(
lmdb-store):Gatsby 在 v3.7 引入了 LMDB 实验性数据存储(见 v3.7 发布说明),将节点数据从纯内存(Redux)改为持久化、可嵌入的存储,以降低大型站点的峰值内存占用。PQR 正是复用了这套 LMDB 数据存储,让主进程与 worker 进程之间可以高效共享数据——worker 不需要把整棵节点树拷贝进自己的内存,而是直接读取持久化存储中的节点。
从构建流程的代码组织也能看到查询运行所处的位置:构建命令在 build.ts 中依次调用runStaticQueries与runPageQueries两个服务(对应 services/index.ts 的导出),其中页面查询服务在 run-page-queries.ts 中实现,其进度活动名称正是日志里看到的run page queries。PQR 开启后,这一阶段的任务会被改派给 worker 进程池执行(日志显示为run queries in workers)。
2.3 官方基准测试:性能提升幅度取决于查询类型
v3.10 发布说明给出了两组实测示例数据,它们来自官方仓库中的 query-filters-sort 基准测试(通过切换特性开关做前后对比,使用GATSBY_CPU_COUNT=5限制为 5 个 worker):
| 查询类型 | 配置 | 开启前(单进程) | 开启后(worker) |
|---|---|---|---|
快速的eq-uniq过滤器 | GATSBY_CPU_COUNT=5 NUM_NODES=100000 NUM_PAGES=10000 FILTER=eq-uniq TEXT=1 | run page queries - 3.787s - 10001/10001 2641.07/s | run queries in workers - 3.445s - 10001/10001 2903.34/s |
较慢的gt过滤器 | GATSBY_CPU_COUNT=5 NUM_NODES=10000 NUM_PAGES=10000 FILTER=gt TEXT=1 | run page queries - 41.832s - 10001/10001 239.07/s | run queries in workers - 15.072s - 10001/10001 663.57/s |
结论要点(以上为 2021 年 7 月官方发布说明中的实测示例,具体提升幅度会随站点类型与运行环境变化):
- 对于本身已经很快的
eq类"快速过滤器"(Fast Filters),并行化带来的提升相对有限(如eq-uniq从约 3.8s 降到约 3.4s); - 对于
gt这类复杂过滤条件、无法走快速路径的查询,提升非常显著(从约 41.8s 降到约 15.1s,吞吐从 239/s 提升到 663/s)。
也就是说:站点使用的查询类型决定了你能获得多大的收益,复杂查询越多,收益越大。
2.4 如何在自己的站点中开启
官方给出的启用步骤分两步:
第一步:确认 Node 版本并安装lmdb-store依赖
PQR 依赖 LMDB 持久化存储,官方要求使用Node v14.10 或更高版本:
npm install lmdb-store第二步:在gatsby-config.js中开启特性标志
module.exports = { flags: { PARALLEL_QUERY_RUNNING: true, }, }2.5 使用官方基准测试复现对比
如果你想在自己的环境里量化 PQR 的收益,可以使用仓库自带的 query-filters-sort 基准测试。该基准会创建指定数量的数据节点并生成指定数量的页面,对多种过滤器(eq、eq-uniq、in、gt、lt、nin、ne、regex等)进行压力测试,并支持可选的排序(SORT)与计数(COUNT)维度。核心环境变量如下:
NUM_NODES:创建的数据节点数量(默认 1000)NUM_PAGES:生成的页面数量(默认 1000,必须 ≤NUM_NODES)FILTER:过滤器类型,例如eq(默认,命中 1/4 节点)、eq-uniq(按唯一值命中单节点)、gt(范围过滤)、regex(正则过滤)等SORT:是否排序,0为不排序(默认),1按随机数排序,也可传逗号分隔的字段列表TEXT:1时为每个节点附加 4KB 随机文本(会增大page-data.json)COUNT:1时在查询中请求totalCount
例如,想要研究gt过滤器的时间复杂度,可以保持页面数不变、逐步增加节点数运行三次:
NUM_NODES=1000 FILTER=gt yarn bench NUM_NODES=10000 FILTER=gt yarn bench NUM_NODES=100000 FILTER=gt yarn bench在gatsby-config.js中切换PARALLEL_QUERY_RUNNING标志即可得到开/关两套日志,用于前后对比。
2.6 已知问题与反馈渠道
官方提示该特性存在一些已知的常见陷阱与解决方案,相关讨论与反馈集中在 PQR 专项讨论帖中;此外,由于 LMDB 依赖原生模块,在 WSL1 环境下运行会遇到类似Error: MDB_BAD_RSLOT: Invalid reuse of reader locktable slot的报错(上游 WSL 问题,见 从 v3 迁移到 v4 的说明),官方建议升级到 WSL2。
三、实验特性二:webpack 持久缓存加速gatsby develop(DEV_WEBPACK_CACHE)
3.1 背景:生产构建已启用,开发模式跟进
Gatsby 在 v3.8 中为生产构建引入了 webpack 5 持久缓存(见 v3.8 发布说明),v3.10 开始将其逐步推广到gatsby develop。持久缓存让 webpack 的编译中间结果落盘复用,从而大幅改善开发服务器的启动时间——第二次及后续启动时无需重新编译全部模块。
3.2 源码层面的缓存配置
在 webpack.config.js 中可以找到这一缓存能力的实现:当构建阶段为build-javascript、build-html、develop或develop-html时,Gatsby 会为 webpack 配置文件系统缓存(type: "filesystem"),缓存位置为站点根目录下.cache/webpack/stage-<阶段名>,并将__filename(webpack 配置本身)与所有实现了onCreateWebpackConfigAPI 的插件路径作为buildDependencies——这意味着当 webpack 配置或相关插件变化时,缓存会自动失效重建。从源码结构可以推断,DEV_WEBPACK_CACHE标志的作用正是让develop与develop-html阶段启用这套持久缓存。
3.3 如何开启
在gatsby-config.js中添加标志即可:
module.exports = { flags: { DEV_WEBPACK_CACHE: true, }, }3.4 与FAST_DEV的关系
如果你已经在使用FAST_DEV标志,那么升级到 Gatsby v3.10 后会自动获得该缓存能力,无需额外配置。从 flags.ts 可以看到,FAST_DEV是一个面向develop命令的"总开关",它通过includedFlags自动附带开启DEV_SSR、PRESERVE_FILE_DOWNLOAD_CACHE等实验,目标是整体改善开发服务器启动时间与开发体验。使用过程中的缓存清理相关问题可在专门的反馈讨论中提交。
四、值得关注的 Bugfix 与改进
除两大实验特性外,v3.10 还包含一系列质量改进(以下为发布说明中列出的官方条目):
gatsby:升级postcss到 8.3.5,消除 Node v16 下的弃用警告。gatsby:将createRoot切换为hydrateRoot(仅在你使用 React 18 时生效)。从当前仓库代码可以印证这一演进:在 cache-dir/app.js 中,Gatsby 会根据是否支持 React 18 分别调用reactDomClient.hydrateRoot或reactDomClient.createRoot;cache-dir/react-dom-utils.js 中同样实现了这两套渲染路径的分支处理。gatsby-source-wordpress:更早地校验预览 URL,并给出更友好的错误反馈(PR #32251)。gatsby:在重定向的最终 URL 以及 Service Worker 更新之后,正确传递window.location的search与hash参数(PR #32334、PR #32323)。gatsby:修复按Ctrl + C退出时出现的UNHANDLED REJECTION write EPIPE错误(PR #32311、PR #32356)。gatsby:当gatsby build因数据缺失等原因失败时,现在会打印对应页面的page-data.json文件内容,以提供更多上下文帮助定位问题(PR #32301)。gatsby-source-contentful:支持从 Image API 读取图片圆角(corner radius)参数(PR #32333)。gatsby-source-contentful:支持metadata.tags属性(PR #31746)。
五、这两个实验特性的后续演进
从仓库中的迁移文档可以清楚看到这两个实验特性的最终去向:
DEV_WEBPACK_CACHE进入核心:在 从 v3 迁移到 v4 的说明 中明确写道,DEV_WEBPACK_CACHE、QUERY_ON_DEMAND、LAZY_IMAGES、FUNCTIONS、PRESERVE_WEBPACK_CACHE这些标志已并入 Gatsby 核心,不再需要通过gatsby-config.js显式开启(也无法再关闭)。PARALLEL_QUERY_RUNNING与DEV_WEBPACK_CACHE标志被移除:在 从 v4 迁移到 v5 的说明 中,两者均被列入已删除的实验标志清单——并行查询运行成为 Gatsby v4/v5 的默认构建行为。- Node 版本要求:v4 起 Gatsby 放弃对 Node 12 的支持,因为底层新依赖
lmdb-store要求 Node>=14.15.0(见 从 v3 迁移到 v4 的说明);v4 之后 Gatsby 也统一使用 LMDB 作为节点数据的持久化存储。
也就是说,如果你现在使用的是 Gatsby v4 或更高版本,这两项能力已经内建并默认生效,无需再配置本文中的实验标志;而如果你仍在使用 v3.x,则可以按照上面的步骤体验这两个实验特性。
六、社区贡献者致谢
v3.10 收到了多位社区贡献者的参与,包括:修复示例中作者名过滤器错误 ID(RapTho)、更新 Storybook 指南至 v6(anselm94)、修复创建源插件教程中缺失的括号(SarthakC)、替换 Google Analytics 域名为 Google Tag Manager(emmanuelgautier)、在swUpdated后传递search/hash到 location(nellaparedes)、修复 Ctrl+C 下的未处理拒绝错误(karlhorky)、更新文档说明翻译暂停状态(weronikadominiak)、修正 apollo/client 包名拼写(cabutler10)、更新 building-a-theme 至最新 Theme UI(ezeYaniv)、修正 JavaScript 拼写(SonnyBrooks),以及为gatsby-source-contentful增加图片圆角与 tags 支持的 axe312ger 等。
相关资源速查
- v3.10 发布说明原文
- Gatsby 构建流程概览
- v3.9 / v3.8 / v3.7 发布说明(同目录下可找到 v3.8、v3.7 等历史版本)
- gatsby-worker 源码与 API 文档
- query-filters-sort 基准测试
- webpack 缓存配置实现
- 从 v3 迁移到 v4 / 从 v4 迁移到 v5
- 前端
- 静态站点
- Web框架
【免费下载链接】gatsby
React-based framework with performance, scalability, and security built in.
相关推荐
Redis vs Memcached:深入解析两大缓存协议的核心特性与实战应用
Redis vs Memcached:深入解析两大缓存协议的核心特性与实战应用 引言:为什么缓存协议如此重要? 在现代分布式系统中,缓存(Cache)已成为提升
文档教程知识库Midscene.js 实战:用自然语言和 AI 视觉驱动跨平台 UI 自动化
Midscene.js 实战:用自然语言和 AI 视觉驱动跨平台 UI 自动化 Midscene.js 是一个 GUI Agent,定位一句话:用自然语言驱动
人工智能AI Agent测试GUI 自动化浏览器控制测试智能体终极指南:如何利用html-webpack-plugin实现Webpack 5持久化缓存配置
终极指南:如何利用html webpack plugin实现Webpack 5持久化缓存配置 html webpack plugin是Webpack生态中最常用
前端前端构建构建工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考