ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Gatsby Node API 参考:gatsby-node 文件机制、完整 API 清单与构建生命周期详解

Gatsby Node API 参考:gatsby-node 文件机制、完整 API 清单与构建生命周期详解 Gatsby Node API 参考gatsby-node 文件机制、完整 API 清单与构建生命周期详解【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby本篇基于 Gatsby 官方参考文档 gatsby-node.md 展开系统讲解gatsby-node.js/gatsby-node.ts文件的运行机制、构建生命周期中全部 Node API 的用途与调用时序、异步操作的三种正确写法并结合 monorepo 中packages/gatsby的源码定位每个 API 的实际触发位置帮助你在构建流程中动态创建页面、扩展 GraphQL 数据层、定制 Babel/Webpack 编译配置并正确管理异步副作用。一、gatsby-node.js 是什么Gatsby 为插件作者和站点构建者提供了大量 API。位于站点根目录的gatsby-node.js或 TypeScript 的gatsby-node.ts中的代码在站点构建流程中执行你可以用它来动态创建页面createPages向 GraphQL 数据层添加数据sourceNodes、createSchemaCustomization、createResolvers等响应构建生命周期中的各类事件onPreBuild、onPostBuild、onCreateDevServer等。使用方法很简单在站点根目录创建gatsby-node.js或gatsby-node.ts文件导出你希望使用的任意 Node API 即可。该文件可以用 JavaScriptCommonJS 或 ES Modules 语法编写也可以用 TypeScript 编写。每个 Node API 都会收到一组辅助函数所有 Gatsby Node API 的回调都会收到一组 helper functions包括reporter输出日志info、warn、panic、activityTimer等graphql查询构建过程中的 GraphQL 数据层actions执行动作createPage、createNode、setWebpackConfig等以及各 API 专属的参数如onCreateNode的node、onCreateDevServer的 Expressapp。一个实现两个 API 的完整示例以下是官方文档给出的gatsby-node.js示例实现了onPostBuild与createPages两个 APIconst path require(path) // 构建完成后打印信息 exports.onPostBuild ({ reporter }) { reporter.info(Your Gatsby site has been built!) } // 动态创建博客页面 exports.createPages async ({ graphql, actions }) { const { createPage } actions const blogPostTemplate path.resolve(src/templates/blog-post.js) const result await graphql( query { allSamplePages { edges { node { slug title } } } } ) result.data.allSamplePages.edges.forEach(edge { createPage({ path: ${edge.node.slug}, component: blogPostTemplate, context: { title: edge.node.title, }, }) }) }文档中的 JSDoc 元数据指向 api-node-docs.ts该文件用 TSDoc 注释完整描述了每一个 Node API 的签名、参数和示例是本文后续 API 清单的第一手依据。二、构建生命周期中 Node API 的调用时序从packages/gatsby源码可以确认各 API 的实际触发位置从而还原出一条清晰的执行时间线onPreInitGatsby 执行期间第一个被调用的 API在插件加载之后、缓存初始化和 bootstrap 准备之前运行。源码触发点在 initialize.tsactivity reporter.activityTimer(onPreInit, { ... }) await apiRunnerNode(onPreInit, { parentSpan: activity.span })onPluginInit在每个进程worker 进程中各执行一次用于存储 action 供后续使用Gatsby 3.9.0 起可用。触发点见 initialize.ts 与 schema 构建入口 entry.ts。onPreBootstrapGatsby 完成初始化、准备 bootstrap 站点时调用initialize.ts。sourceNodes在 bootstrap 阶段由源码插件调用以创建节点。它对每个插件恰好调用一次如果定义在gatsby-node.js中则会在所有 source 插件完成创建节点之后恰好调用一次。onPostBootstrapbootstrap 流程的最后一步在其他扩展 API 之后调用随后发出BOOTSTRAP_FINISHED事件。源码在 post-bootstrap.ts其中可以看到它先await apiRunnerNode(onPostBootstrap, ...)再结束 activity timer。onCreateNode每创建一个新节点就会调度回调插件可借此扩展或转换其他插件创建的节点。触发点在 plugin-runner.ts。Gatsby 5.0.0 起新增shouldOnCreateNode守卫 API在调度onCreateNode之前调用返回 falsy 则跳过该节点——注意它不接收常规的api首参// 仅对 Image 节点处理 exports.shouldOnCreateNode ({ node }, pluginOptions) node.internal.type ImageonPreBuild/onPostBuild构建过程的首尾两个扩展点。onPreBuild在 bootstrap 完成后、build 步骤开始前调用onPostBuild在所有构建步骤完成后调用。二者均在 commands/build.ts 与 commands/build.ts 中通过apiRunnerNode触发。onPostBuild还可用basePath、pathPrefix参数exports.onPostBuild ({ reporter, basePath, pathPrefix }) { reporter.info( Site was built with basePath: ${basePath} pathPrefix: ${pathPrefix} ) }查询提取阶段onPreExtractQueries在从 JS 文件提取 GraphQL 查询/片段之前运行适合插件添加更多含查询的 JS 文件例如来自node_modules的文件触发点见 extract-queries.ts。开发服务器onCreateDevServer在gatsby develop启动服务器时运行可向其 Expressapp添加代理和中间件触发点在 start-server.tsexports.onCreateDevServer ({ app }) { app.get(/hello, function (req, res) { res.send(hello world) }) }createPages在初次节点 sourcing/transform 以及 GraphQL schema 构建全部完成之后才被调用因此你可以查询数据来创建页面也可以从远程或本地源获取数据来创建页面。createPages动作本身的实现含 JSDoc 示例位于 public.jssourceNodes动作实现位于同文件 public.js。所有 API 回调最终都经由 api-runner-node.js 分发——该文件还负责为每个插件绑定double-bindRedux actions使插件发起的每个动作都自动携带插件元数据与 traceId便于追踪和调试。三、Node API 完整清单以下按功能分组列出文档所关联的全部 Node API签名与示例均取自 api-node-docs.ts 中的 JSDoc。3.1 页面创建类API用途备注createPages动态创建页面在 sourcing/transform 与 schema 构建完成后调用可查询 GraphQL 或拉取远程数据createPagesStatefully有状态地管理页面的增删实现它的插件不会被定期重新调用来重算页面信息需要自行维护状态onCreatePage新页面被创建时回调可用于修改其他插件创建的页面如去掉路径尾部斜杠createPages的完整示例查询 Markdown 节点并创建博客页支持 GraphQL 变量const path require(path) exports.createPages ({ graphql, actions }) { const { createPage } actions const blogPostTemplate path.resolve(src/templates/blog-post.js) // Variables can be added as the second function parameter return graphql( query loadPagesQuery ($limit: Int!) { allMarkdownRemark(limit: $limit) { edges { node { frontmatter { slug } } } } } , { limit: 1000 }).then(result { if (result.errors) { throw result.errors } // Create blog post pages. result.data.allMarkdownRemark.edges.forEach(edge { createPage({ // Path for this page — required path: ${edge.node.frontmatter.slug}, component: blogPostTemplate, context: { // context 数据会作为 props 传入页面组件 // 也可作为页面 GraphQL 查询的变量 // 页面 path 始终可作为 GraphQL 变量使用 }, }) }) }) }createPagesStatefully的典型使用者是 gatsby-plugin-page-creator它监视src/pages目录中 JS 页面的增删。由于其事实来源pages 目录不为 Gatsby 所知它需要自己维护状态来知道何时添加/删除页面。onCreatePage有一个重要细节Gatsby 内置了防循环机制——同一个gatsby-node.js创建的页面不会再次触发该文件的onCreatePage避免无限回调。3.2 数据层sourcing 与节点处理exports.sourceNodes ({ actions, createNodeId, createContentDigest }) { const { createNode } actions const myData { key: 123, foo: The foo field of my node, bar: Baz } const nodeContent JSON.stringify(myData) const nodeMeta { id: createNodeId(my-data-${myData.key}), parent: null, children: [], internal: { type: MyNodeType, mediaType: text/html, content: nodeContent, contentDigest: createContentDigest(myData) } } const node Object.assign({}, myData, nodeMeta) createNode(node) }onCreateNode用于扩展/转换其他插件创建的节点可配合createNode、createNodeField动作使用exports.onCreateNode ({ node, actions }) { const { createNode, createNodeField } actions // 在这里转换新节点创建新节点或新增节点字段 }仓库中完整的源插件编写教程见 creating-a-source-plugin 目录可作为sourceNodesonCreateNode组合使用的实操参考。3.3 GraphQL schema 定制setFieldsOnGraphQLNodeType在创建 GraphQL schema 期间调用允许插件为数据节点推导出的类型添加新字段每个类型会被单独调用一次。函数应返回符合GraphQLFieldConfigMap形状的对象。注意必须从gatsby/graphql导入 GraphQL 类型而不要把graphql包加入依赖否则会报Schema must contain unique named types...错误gatsby/graphql还额外导出了GraphQLJSON类型import { GraphQLString } from gatsby/graphql exports.setFieldsOnGraphQLNodeType ({ type }) { if (type.name File) { return { newField: { type: GraphQLString, args: { myArgument: { type: GraphQLString }, }, resolve: (source, fieldArgs) { return Id of this node is ${source.id}. Field was called with argument: ${fieldArgs.myArgument} } } } } // 默认返回空对象 return {} }许多 transformer 插件用它添加带参数的字段gatsby-transformer-remark借此提供可指定截取字符数的excerpt字段gatsby-transformer-sharp暴露了大量图片转换选项字段。createSchemaCustomizationGatsby 2.12.0 起通过createTypes、createFieldExtension、addThirdPartySchema三个动作定制 schema——这三个动作只在该 API 中可用。它在 schema 生成之前立即运行若需要修改已生成的 schema例如定制第三方类型应改用createResolversexports.createSchemaCustomization ({ actions }) { const { createTypes, createFieldExtension } actions createFieldExtension({ name: shout, extend: () ({ resolve(source, args, context, info) { return String(source[info.fieldName]).toUpperCase() } }) }) const typeDefs type MarkdownRemark implements Node dontInfer { frontmatter: Frontmatter } type Frontmatter { title: String! tagline: String shout date: Date dateformat image: File fileByRelativePath } createTypes(typeDefs) }createResolversGatsby 2.2.0 起为 GraphQL schema 添加自定义字段 resolver。要点来自 JSDoc 原文不允许覆盖已有字段类型需要改类型请用createTypes第三方 schema 添加的类型例外新字段不会出现在filter/sort输入类型上如需请在createTypes定义的类型上扩展字段配置中类型可以用字符串引用扩展已有 resolver 的字段时原 resolver 可从info.originalResolver访问该 API 是 schema 生成的最后一步因此可通过intermediateSchema参数拿到中间 schemaresolver 函数内建议从info.schema访问最终 schema数据层含内部查询能力暴露在context.nodeModel上findOne、getNodeById、getNodesByIds直查节点库findAll做更高级查询可以为根Query类型添加字段使用第一个 resolver 参数source也常称parent/root时注意字段 resolver 在一次查询中可能被调用多次如字段同时出现在输入过滤器和选择集中source上的外键字段可能处于已解析或未解析状态。exports.createResolvers ({ createResolvers }) { const resolvers { Author: { fullName: { resolve: (source, args, context, info) { return source.firstName source.lastName }, }, }, Query: { allRecentPosts: { type: [BlogPost], resolve: async (source, args, context, info) { const { entries } await context.nodeModel.findAll({ type: BlogPost }) return entries.filter(post post.publishedAt Date.UTC(2018, 0, 1)) }, }, }, } createResolvers(resolvers) }更完整的示例可参考仓库中的 using-type-definitions 示例站。3.4 编译配置与杂项API用途说明onCreateWebpackConfig扩展/修改站点 webpack 配置可用stagedevelop、develop-html、build-javascript、build-html之一、getConfig、rules、loaders、actionsonCreateBabelConfig通过setBabelPlugin/setBabelPreset扩展 Babel 配置同样提供stage与actionspreprocessSource让 compile-to-js 插件把源码处理成 JS供 query runner 提取 GraphQL 查询供编译器插件实现resolvableExtensions让 compile-to-js 插件把其扩展名加入可解析列表Gatsby 默认支持.js和.jsxpluginOptionsSchema用 Joi 定义并校验插件选项Gatsby 2.25.0 起bootstrap 阶段运行参数为{ Joi }onCreateWebpackConfig官方示例exports.onCreateWebpackConfig ({ stage, getConfig, rules, loaders, actions }) { actions.setWebpackConfig({ module: { rules: [ { test: my-css, use: [loaders.style(), loaders.css()], }, ], }, }) }pluginOptionsSchema官方示例exports.pluginOptionsSchema ({ Joi }) { return Joi.object({ // 校验 anonymize 选项必须由用户提供且是布尔值 anonymize: Joi.boolean().required(), }) }四、异步 vs 同步三种正确写法这是原文档中极易踩坑的关键章节如果你的插件或站点gatsby-node.js执行异步操作磁盘 I/O、数据库访问、调用远程 API 等必须让 Gatsby 知道何时完成——要么返回 Promise显式Promise或async/await要么调用传入回调的第三个参数cb。因为部分 API 要正确工作必须等待前序 API 完成// 写法一Async/await exports.createPages async () { // do async work const result await fetchExternalData() } // 写法二Promise API exports.createPages () { return new Promise((resolve, reject) { // do async work }) } // 写法三Callback APIcb 是第三个参数 exports.createPages (_, pluginOptions, cb) { // do async work cb() }如果你的插件不做异步工作直接同步返回即可。异步生命周期问题的排查方法见 Debugging Async Lifecycles。从源码机制看onPostBuild之后 build.ts 还会专门wait所有在onPostBuild中启动的 jobs——这正是插件必须告知 Gatsby 何时完成的工程化保障。五、源码级要点小结API 元数据来源api-node-docs.ts 中每个export const xxx true声明一个 Node APIJSDoc 即其权威文档含gatsbyVersion标注的引入版本createResolvers2.2.0、createSchemaCustomization2.12.0、pluginOptionsSchema2.25.0、onPluginInit3.9.0、shouldOnCreateNode5.0.0。API 分发机制所有回调经 api-runner-node.js 执行actions 按插件双重绑定以注入元数据createContentDigest被覆写为剔除自动生成字段保证 digest 一致性。辅助函数文档node-api-helpers.md 详解reporter、graphql、actions等每个 API 首参对象中的成员。TypeScript 支持gatsby-node.ts的GatsbyNode类型让每个导出 API 都有完整类型提示设置方法见 TypeScript 文档ESM 语法写法见 ES Modules 文档。掌握了以上 API 清单、生命周期时序与异步契约你就可以在gatsby-node.js中完成从数据 sourcing、schema 定制到页面动态创建的全部构建期逻辑并在出现异常时依据各 API 的源码触发点快速定位问题发生的阶段。【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表