多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

在 Gatsby 中创建本地插件(Local Plugin):从 plugins 文件夹到源码级加载机制

在 Gatsby 中创建本地插件(Local Plugin):从 plugins 文件夹到源码级加载机制 在 Gatsby 中创建本地插件Local Plugin从 plugins 文件夹到源码级加载机制【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby如果你开发的插件只服务于当前站点的特定场景或者你正在开发一个插件并希望简化迭代流程那么**本地插件local plugin**是 Gatsby 中最顺手的组织方式它不依赖 npm 发布直接把插件代码放进项目的plugins文件夹即可被gatsby develop/gatsby build加载。本文以 Gatsby 官方文档 docs/docs/creating-a-local-plugin.md 为核心结合仓库中examples/using-local-plugins、examples/using-multiple-local-plugins两个示例站点以及packages/gatsby/src/bootstrap/load-plugins下的源码实现系统讲解本地插件的目录约定、四种加载方式、Babel 编译注意事项与底层解析机制读完你可以独立完成一个本地插件的搭建、外部开发与调试。什么时候应该使用本地插件官方文档给出了两个典型场景插件只与你的具体用例相关功能高度定制、没有通用价值没必要发布到 npm正在开发一个插件想要更简单的开发流程把代码放在站点内改完即生效省去npm pack/npm link/ 版本发布的循环。此外开发 fork 自社区插件的定制版本或准备把插件发布为独立包也可以先以本地插件的形式进行测试与迭代。只要满足上述场景plugins文件夹就是最便捷的载体。本地插件的目录结构约定官方文档要求把插件代码放在项目根目录的plugins文件夹中结构如下/my-gatsby-site └── gatsby-config.js └── /src └── /plugins └── /my-own-plugin └── package.json仓库中的真实示例 examples/using-local-plugins 与之完全对应——一个名为gatsby-source-pokeapi的本地 source 插件位于plugins/gatsby-source-pokeapi/站点其余代码在src/下examples/using-local-plugins/ ├── gatsby-config.js ├── gatsby-node.js ├── package.json ├── /plugins │ └── /gatsby-source-pokeapi │ ├── gatsby-node.js │ └── package.json └── /src └── /templates ├── ability.js ├── all-pokemon.js └── pokemon.js需要注意两点必须显式在gatsby-config.js中注册插件——Gatsby 对本地插件没有自动探测机制不写进配置就不会加载gatsby-config.js中使用的名称必须与插件文件夹的根目录名一致而不是package.json里的name字段。例如上面的示例配置里写的是gatsby-source-pokeapi目录名也必须是gatsby-source-pokeapi。配置示例来自 examples/using-local-plugins/gatsby-config.jsmodule.exports { plugins: [gatsby-source-pokeapi], }它可以与任何第三方 Gatsby 插件并列注册例如module.exports { plugins: [ gatsby-third-party-plugin, my-own-plugin, // highlight-line ], }源码层面本地插件如何被识别这种“目录名即插件名”的约定并非文档凭空规定而是由加载器代码强制实现的。在 packages/gatsby/src/bootstrap/load-plugins/utils/check-local-plugin.ts 中本地插件的判定逻辑是将配置中出现的插件名拼接为rootDir/plugins/pluginName路径检查该路径是否存在不存在则判定为「非本地插件」检查package.json是否存在缺少package.json会直接抛出异常Local plugin ${pluginName} requires a package.json file。也就是说插件目录名必须与配置中的引用名严格一致且每个本地插件都必须自带package.json这是硬性约束。随后在 resolve-plugin.ts 中加载器会读取该package.json用packageJSON.name || pluginName作为插件名生成插件 ID 与版本号version字段缺失时甚至会用目录内容哈希兜底并调用getResolvedFieldsForPlugin解析各gatsby-*文件的编译产物最终把本地插件与 node_modules 中的第三方插件统一成同一个IPluginInfo结构进入后续管线。注册后插件可以接入哪些 API把插件名写入gatsby-config.js后插件就能通过 Gatsby 的Node API与SSR API挂钩Node APIgatsby-node.js在构建期执行典型用途包括sourceNodes抓取数据并创建节点、createPages从数据生成页面、onPreInit构建早期钩子等SSR APIgatsby-ssr.js用于服务端渲染阶段替换或注入内容例如replaceRenderer、onRenderBody。examples/using-local-plugins 中的gatsby-source-pokeapi就是一个完整的 Node API 使用范例它的 gatsby-node.js 通过exports.sourceNodes从 PokéAPI 的 REST 接口拉取数据用createNodeaction 把「宝可梦」与「特性」处理成 Gatsby 的 node 格式写入 GraphQL 数据层再通过abilities___NODE建立节点间关联——本地插件完全可以承担与第三方插件同等的完整功能。在项目外部开发本地插件插件不必局限在站点项目内部。如果你打算把插件发布为独立包或者要测试/开发社区插件的 fork 版本可以先把插件从站点「解耦」再通过以下方式之一让站点引用它。方式一用官方 starter 快速生成插件骨架官方提供了专门的插件 starter一条命令即可生成独立插件项目gatsby new gatsby-plugin-foo https://github.com/gatsbyjs/gatsby-starter-plugin对应的 starter 源码就在仓库的 starters/gatsby-starter-plugin 中生成后即可获得带gatsby-node.js等文件的标准插件目录结构方便直接开始外部开发。方式二require.resolve 相对路径引用不想依赖plugins文件夹时可以在gatsby-config.js中通过require.resolve直接指向插件路径路径相对于gatsby-config.js所在位置module.exports { plugins: [ gatsby-plugin-react-helmet, // highlight-start { // including a plugin from outside the plugins folder needs the path to it resolve: require.resolve(../path/to/gatsby-local-plugin), }, // highlight-end ], }仓库示例 examples/using-multiple-local-plugins 的站点配置 gatsby-site-using-local-plugins/gatsby-config.js 中就是这么做的——gatsby-plugin-console-log-b位于独立项目目录通过resolve: require.resolve(../gatsby-plugin-console-log-b)从插件文件夹之外被加载其 gatsby-node.js 实现了exports.onPreInit在构建早期向控制台打印一行日志。方式三npm link或yarn link符号链接通过npm link/yarn link可以把机器上其他位置的插件包软链到当前站点npm link ../path/to/my-plugin该命令需要在 Gatsby 站点根目录下执行执行后你的站点就能像引用普通 node_modules 包一样引用该插件。这与「通过 yarn workspaces 开发 Gatsby 主题」的做法类似主题开发的推荐方式workspace 的搭建步骤可以参考 Building a Theme 指南。using-multiple-local-plugins示例对这种方式给出了完整的实操闭环先把配置中的gatsby-plugin-console-log-c取消注释示例站点中默认注释掉了该行然后在站点根目录执行npm link ../gatsby-plugin-console-log-c再运行gatsby develop输出中会依次出现三个插件各自打印的日志验证三种加载方式全部生效$ gatsby develop success open and validate gatsby-configs - 0.051s success load plugins - 1.047s logging to the console from plugins folder logging to the console from a plugin in another project with require.resolve logging to the console from a plugin in another project with npm/yarn link logging to the console from sites gatsby-node success onPreInit - 0.023s四种加载模式一览using-multiple-local-plugins示例把同一个onPreInit日志插件实现了 4 份恰好覆盖了全部加载形态站点自身的 gatsby-node.js 也算一种模式代码位置引用方式示例站点自身的 gatsby-node站点根目录gatsby-node.js无需注册天然执行gatsby-site-using-local-plugins/gatsby-node.jsplugins 文件夹中的插件站点内plugins/name/配置中写插件名gatsby-plugin-console-log-a插件文件夹之外的插件独立项目目录resolve: require.resolve(相对路径)gatsby-plugin-console-log-b外部插件符号链接独立项目目录npm link/yarn link后写插件名gatsby-plugin-console-log-c理解底层Gatsby 是如何加载本地插件的为了让「为什么目录名必须和配置一致」「为什么必须写package.json」这些问题有据可依可以深入加载管线一探究竟。本地插件的解析路径位于packages/gatsby/src/bootstrap/load-plugins/目录下核心链路如下校验与规整validate.ts 与 process-plugin.ts 负责把gatsby-config.js中字符串形式的插件名规整成统一的插件对象PluginRef本地插件判定check-local-plugin.ts 拼接rootDir/plugins/pluginName并做存在性检查同时强制要求package.json存在解析并读取元信息resolve-plugin.ts 读取本地插件的package.json填充插件名、ID、版本缺版本时用目录内容哈希兜底再通过getResolvedFieldsForPlugin解析各gatsby-*入口文件的编译产物兜底逻辑如果上述本地路径都匹配不上resolve-plugin.ts 会退而求其次尝试从node_modules中require.resolve解析或解析绝对路径指向的内置插件解析失败则抛出构建错误并提示 Perhaps you need to install its package?。这也从实现上印证了本地插件与第三方插件的加载路径是同一管线内的两条分支。从源码结构可以推断Gatsby 在启动引导bootstrap阶段就完成了插件的统一收集与编译准备之后再按生命周期逐个执行各插件的 Node API / SSR API因此插件的gatsby-*文件是否可被当前 Node 版本解析直接影响加载是否成功——这正是下一节要讨论的 Babel 编译问题的由来。Babel 编译与处理哪些文件会被转译官方文档在编译与处理方面给出了明确的边界这也是本地插件开发中最容易踩的坑除了gatsby-browser.js会作为 webpack 打包流程的一部分被处理之外其余所有gatsby-*文件如gatsby-node.js、gatsby-ssr.js不会经过 Babel 处理。这意味着gatsby-node.js与gatsby-ssr.js会原样交给 Node.js 执行如果你的代码使用了当前 Node 版本不支持的 JavaScript 语法例如较新的 ESM 语法或实验性特性运行时会直接报错唯一的例外是gatsby-browser.js因为它最终会随 webpack 打包进浏览器端 bundle所以会走 Babel 转译可行的规避方案把源码写在插件的src子文件夹中自行构建compile/build后再把产物输出到插件根目录。即「源码放src/构建后产物放插件根目录」让 Node 直接加载构建后的版本。从源码侧看这一约束与 resolve-plugin.ts 中调用的getResolvedFieldsForPlugin定义于 packages/gatsby/src/utils/parcel/compile-gatsby-files.ts相呼应——Gatsby 会针对每个插件的gatsby-*入口做编译解析而浏览器端文件与 Node 端文件分属不同编译路径Node 端文件不会被 Babel 转译。因此本地插件的 Node 端代码请尽量使用当前 Node 版本原生支持的语法或自行在src中编写并构建到根目录。动手实践从零创建一个本地插件结合官方文档与两个示例仓库完整的实操路径如下第 1 步创建目录结构与 package.json/my-gatsby-site └── gatsby-config.js └── /src └── /plugins └── /my-own-plugin └── package.json └── gatsby-node.jspackage.json是最低要求源码强制校验内容可参考 examples/using-local-plugins/plugins/gatsby-source-pokeapi/package.json 的最简形态{ name: my-own-plugin, version: 1.0.0, main: index.js }注意name字段可以与目录名不同但加载时必须以目录名为准。第 2 步注册到 gatsby-config.jsmodule.exports { plugins: [ // ... 其他第三方插件 my-own-plugin, ], }第 3 步编写插件逻辑在插件根目录新建gatsby-node.js实现需要的 Node API。最简单的验证可以从onPreInit开始参考 examples/using-multiple-local-plugins/gatsby-plugin-console-log-b/gatsby-node.jsexports.onPreInit () { console.log(my local plugin is loaded) }如果要做真正的 source 插件可以参考gatsby-source-pokeapi的 gatsby-node.js用exports.sourceNodes抓取外部数据并用createNode写入 GraphQL 层。第 4 步运行验证npm install gatsby develop如果配置正确gatsby develop输出的插件加载阶段success load plugins之后、success onPreInit之前就能看到你插件打印的日志如果报错提示找不到插件或缺少package.json请优先检查目录名与配置名是否一致、package.json是否存在、路径require.resolve模式是否相对gatsby-config.js写对了。小结本地插件是 Gatsby 插件开发与定制化集成的低成本入口放进plugins文件夹并写入gatsby-config.js即可被加载需要独立发布时可以改用require.resolve相对路径引用或npm link/yarn link符号链接来解耦。记住三条关键规则即可规避绝大多数问题——配置中的名称必须等于插件目录名、每个本地插件必须自带package.json、除gatsby-browser.js外的gatsby-*文件不会经过 Babel 处理。仓库中的 using-local-plugins完整 source 插件与 using-multiple-local-plugins四种加载模式对照是随文档配套的最佳实践参考配合 load-plugins 源码 阅读即可对本地插件的加载机制建立从用法到原理的完整认知。【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表