ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Gatsby YAML 单文件基准测试站点(gabe-yaml-text)深入解析

Gatsby YAML 单文件基准测试站点(gabe-yaml-text)深入解析 Gatsby YAML 单文件基准测试站点gabe-yaml-text深入解析【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby本文基于 benchmarks/gabe-yaml-text/README.md 展开。该站点是 Gatsby 官方仓库中 Gabe 基准测试Benchmark矩阵的一员专门用于度量「单个 YAML 文件驱动全站页面生成」场景下 Gatsby 的构建性能。读完本文你将掌握该基准站点的完整工作原理如何用伪随机数据批量生成 YAML 数据文件、如何通过gatsby-source-filesystemgatsby-transformer-yaml把单文件 YAML 变成 Gatsby 数据层节点、如何按N/M环境变量控制页面规模与 Node 堆内存来执行可复现的yarn bench基准跑分并能结合源码理解其底层调用链。一、Gabe 基准测试项目与本站定位benchmarks/目录是 Gatsby 官方用于性能基准测试的示例站点集合见 benchmarks/README.md其中gabe-*系列gabe-csv-text、gabe-fs-text、gabe-json-text、gabe-yaml-text、gabe-fs-markdown、gabe-fs-mdx等构成一个「数据源 × 内容格式」的测试矩阵。gabe-yaml-text是其中的基线Baseline站点数据形态所有页面的内容数据全部存放在单个 YAML 文件gendata.yaml中页面形态可生成任意数量的超简单页面每页只有一个小标题header、一句引言quote和两小段随机文本刻意不放图片测试目标README 明确说明「No images, because we want to benchmark the yaml transformer」——不放图片是为了把构建耗时聚焦到 YAML 转换环节本身避免图片处理干扰对 YAML 转换性能的度量。该站点在仓库中的关键文件如下文件职责gen.js伪随机生成gendata.yaml数据文件gatsby-config.js配置gatsby-source-filesystem与gatsby-transformer-yamlgatsby-node.js从 YAML 节点批量createPagesrc/pages/index.js首页文章列表src/templates/blog-post.js文章详情模板package.json定义bench基准脚本二、安装与运行一条命令跑完整个基准2.1 安装依赖与仓库其他基准站点一致进入该目录后安装依赖即可cd benchmarks/gabe-yaml-text yarn # 或 npm install依赖清单见 package.jsongatsby、gatsby-source-filesystem、gatsby-transformer-yaml、react/react-dom以及用于生成伪随机数据的faker^4.1.0。2.2 启动一次基准跑分README 给出的标准命令是N1000 M2 yarn bench两个环境变量的含义N1000指示本次运行构建一个1000 个页面的站点M2指示 Node.js 为长期存储V8 老生代堆max_old_space_size最多使用 2GB 内存。如果不传任何参数默认的yarn bench将构建512 个页面README 同时说明其默认内存描述为 1GB实际以脚本为准见下节。2.3yarn bench内部到底执行了什么README 只概括了流程真正的执行细节在 package.json 的bench脚本中bench: rm -rf gendata.yaml; gatsby clean; N${N:-512} node gen.js; CI1 node --max_old_space_size${M:-2}000 node_modules/.bin/gatsby build拆开看它依次完成四件事清理上一次生成的 YAML 文件rm -rf gendata.yaml保证每次跑分都从干净状态开始这也符合 benchmarks/README.md 中「previous benchmark runs do not interfere with the current run」的接口约定清空 Gatsby 缓存gatsby clean生成 N 个页面的伪随机内容N${N:-512} node gen.js——${N:-512}表示未设置N时默认生成 512 条数据随后gen.js会逐条把文章追加写入gendata.yaml以 CI 模式执行生产构建CI1 node --max_old_space_size${M:-2}000 node_modules/.bin/gatsby build。其中有两处值得注意的细节CI1强制 Gatsby 以 CI非交互模式构建避免开发态交互逻辑干扰计时让基准结果更纯净--max_old_space_size${M:-2}000是一个字符串拼接技巧${M:-2}未设置时取默认值2拼上000即得到2000单位 MB。也就是说从当前脚本看默认堆上限约 2GBREADME 中「默认 1GB 内存」的描述与脚本存在出入跑分时建议以脚本实际行为为准按需显式指定M。另外该站点还有build、clean、develop、format等常规脚本develop可启动开发服务器手动检查生成的页面效果。三、数据生成器 gen.js如何在单文件里产出 N 篇文章gendata.yaml不是手工维护的而是由 gen.js 在每次yarn bench时用faker库即时生成。3.1 文章结构createArticle(n, sentence, slug)返回一篇「文章」对象function createArticle(n, sentence, slug) { const desc faker.lorem.sentence() return { articleNumber: String(n), title: sentence, description: desc, slug, date: faker.date.recent(1000).toISOString().slice(0, 10), html: [faker.lorem.paragraphs(), faker.lorem.paragraphs()], } }字段设计刻意模拟真实博客articleNumber序号、title标题来自faker.lorem.sentence()、description引言、slugURL 路径、date近 1000 天内的随机日期截取为YYYY-MM-DD、html两段随机段落组成的数组。其中html是数组后面我们会看到它在 YAML 里被写成列表最终映射到模板中的两段p。3.2 写文件的策略先清空、再逐条追加主流程是一个异步 IIFEconst N parseInt(process.env.N, 10) || 100 ... await fs.writeFile(gendata.yaml, ) // Replace contents, regardless for (let i 0; i N; i) { ... await fs.appendFile(gendata.yaml, ...) }N从环境变量读取parseInt失败或未设置时兜底为 100注意yarn bench脚本里会用${N:-512}覆盖为默认 512先writeFile清空文件「Replace contents, regardless」与脚本中的rm -rf双保险再用appendFile逐条追加避免一次性把 N 条内容全部拼进内存。3.3 序列化的关键双引号风格与转义每条文章被序列化为如下 YAML 片段外层是一个- data:数组项- data:\n Object.keys(page) .map(key { const v page[key] if (Array.isArray(v)) { return key :\n v.map(v - v.replace(/([\\])/g, \\$1) ).join(\n) } return key : v.replace(/([\\])/g, \\$1) }) .join(\n) \n即每个页面是一个- data:块块内每行key: value数组字段html则展开为key:下缩进的- ...列表。代码注释里引用了 YAML 1.2 规范 关于双引号风格的说明并解释了选择双引号的原因plain style纯量裸值没有任何转义手段遇到冒号、引号等指示符字符容易出错双引号风格是唯一能用\转义序列表达任意字符串的风格代价是需要转义\和本身因此这里用v.replace(/([\\])/g, \\$1)统一转义与\再包上双引号输出注释还特别说明双引号值会保留换行但裁剪行尾空格这对用于渲染 HTML 的段落文本是可接受的。生成结果的形状大致是- data: articleNumber: 0 title: Nihil et accusamus dolores... description: Qui velit... slug: nihil-et-accusamus-dolores date: 2023-05-12 html: - 第一段 lorem 文本 - 第二段 lorem 文本- data:是代码中的固定前缀后续会被解析成一个 YAML 根数组。四、数据接入单文件 YAML 如何进入 Gatsby 数据层4.1 gatsby-config.js两个插件接力gatsby-config.js 非常精简核心是两个插件module.exports { siteMetadata: { title: Gatsby FS YAML Benchmark for Gabe, description: A blog like no other blog, author: Bob the Blogger, }, plugins: [ gatsby-transformer-yaml, { resolve: gatsby-source-filesystem, options: { name: blog, path: ${__dirname}/gendata.yaml, }, }, ], }gatsby-source-filesystem直接把gendata.yaml这个文件本身而不是目录注册为源path用__dirname定位到基准站点目录gatsby-transformer-yaml负责把该文件的text/yaml内容转换成一个个 GraphQL 节点。4.2 底层原理gatsby-transformer-yaml 的转换逻辑整个数据链路的「心脏」是 packages/gatsby-transformer-yaml/src/gatsby-node.js。可以从中提炼三个关键事实触发条件shouldOnCreateNode只对node.internal.mediaType text/yaml的节点生效确保不会误伤其他文件类型function shouldOnCreateNode({ node }) { return node.internal.mediaType text/yaml }解析方式onCreateNode用js-yaml的jsYaml.load(content)解析文件内容然后按结果形态分流——根级数组则逐项transformObject根级普通对象则整体转成一个节点。gendata.yaml的根是一个数组每一行- data:构成一项因此每篇文章会被转换成一个独立节点const parsedContent jsYaml.load(content) if (_.isArray(parsedContent)) { parsedContent.forEach((obj, i) { transformObject( obj, createNodeId(${node.id} [${i}] YAML), getType({ node, object: obj, isArray: true }) ) }) } else if (_.isPlainObject(parsedContent)) { ... }节点类型命名节点类型名由getType推导。对于File节点且内容为数组的情况规则是_.upperFirst(_.camelCase(\${node.name} Yaml))。本站点数据文件名为gendata.yamlnode.name即gendata数组形态会拼成Gendata Yaml→ camelCase 后GendataYaml。**这正是后面 GraphQL 查询中出现allGendataYaml/gendataYaml 类型名的来源**。每个转换出的 YAML 节点都会被createNode写入数据层并通过createParentChildLink挂到源File节点之下。五、页面生成从 YAML 节点到静态页面5.1 gatsby-node.js批量 createPagebenchmarks/gabe-yaml-text/gatsby-node.js 的createPages先查询全部文章节点再逐条创建页面const result await graphql( query { allGendataYaml { nodes { id slug title # used in prev/next } } } ) ... posts.forEach(({ id, slug }, index) { const previous index posts.length - 1 ? null : posts[index 1] const next index 0 ? null : posts[index - 1] createPage({ path: slug, component: blogPost, context: { id, slug, previous, next }, }) })每篇文章的 URL 直接采用gen.js生成的slug页面模板统一指向src/templates/blog-post.js通过context传入previous/next相邻文章信息用于详情页底部的上一篇/下一篇导航以及用于精确查询的id。5.2 文章模板按 id 取单条数据blog-post.js 通过pageContext.id精确定位单篇文章export const pageQuery graphql query BlogPostById($id: String!) { site { siteMetadata { title } } gendataYaml(id: { eq: $id }) { title description date(formatString: MMMM DD, YYYY) html } } 渲染时把description放进blockquotehtml数组的两段分别渲染成两个p使用dangerouslySetInnerHTML底部用pageContext.previous/next渲染上一页/下一页链接并复用Bio组件见 src/components/bio.js。5.3 首页列表查询index.js 用allGendataYaml查询文章列表limit: 100按日期倒序渲染成首页的文章摘要列表——这正是N很大时例如 1000 篇用来检验列表页查询/渲染性能的部分。六、如何解读一次基准运行运行N1000 M2 yarn bench后终端会依次输出gen.js的生成日志Start of gen/Now generating 1000 articles/Finished generating 1000 articles/End of gen以及gatsby build的构建日志。衡量该基准站点性能时可关注生成阶段耗时gen.js顺序追加写 1000 条记录的开销YAML 解析/建节点耗时gatsby-transformer-yaml用js-yaml解析 1000 项数组并逐项建节点的开销README 强调不放图片正是为了把该环节的耗时「暴露」出来页面生成耗时createPages创建 1000 个页面、模板查询与渲染的开销。对比实验建议将N固定、分别运行本站点YAML 单文件与 gabe-csv-text、gabe-json-text、gabe-fs-markdown 等同规模兄弟站点即可横向比较不同数据源格式在相同页面规模下的构建性能差异。注意保持M堆内存上限一致否则 GC 行为不同会污染对比结果。七、小结gabe-yaml-text是一个「小而完整」的 Gatsby 性能探针gen.js用可复现的伪随机内容按N生成单文件 YAMLgatsby-source-filesystemgatsby-transformer-yaml将其转换为GendataYaml节点gatsby-node.js批量建页模板按id精确查询渲染。通过N/M两个环境变量即可控制页面规模与 Node 堆内存完成一次可重复的 YAML 转换性能基准。其完整链路——从 gen.js 的双引号转义序列化、到 gatsby-transformer-yaml 的类型命名与数组拆分、再到 blog-post.js 的页面查询——既是基准测试的标准模板也是一份理解「文件源 → 转换器 → 页面」Gatsby 数据流的最佳入门范例。【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表