Gatsby gatsby-plugin-netlify-cms 深度解析:自动生成 Netlify CMS 管理面板的原理、参数与源码实现
Gatsby gatsby-plugin-netlify-cms 深度解析自动生成 Netlify CMS 管理面板的原理、参数与源码实现【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby本文以 Gatsby 仓库中已废弃的gatsby-plugin-netlify-cms插件的官方 README 为核心完整覆盖其安装方式、全部配置参数与典型示例并结合仓库内deprecated-packages/gatsby-plugin-netlify-cms/src下的真实源码剖析它如何通过onCreateWebpackConfig独立构建一套 webpack 流程、把admin/index.html生成到站点public目录以及 Identity 登录回调为何能在普通 Gatsby 页面上被处理。读完本文你可以完整理解该插件每个选项在构建层的作用机制并知道如何将其迁移到继任者gatsby-plugin-decap-cms。一、先说结论该插件已废弃README 开头的声明是阅读本包的前提gatsby-plugin-netlify-cms已被废弃插件已更名并迁移到 Decap CMS 项目继任包为gatsby-plugin-decap-cms。因此本插件在仓库中位于deprecated-packages/目录而非活跃的packages/目录。同时README 给出了一个对老项目排障很重要的版本对应关系这是原 README 的核心信息之一完整继承Gatsby 版本Netlify CMS 版本需要的gatsby-plugin-netlify-cms版本Gatsby v1Netlify CMS 1.x^2.0.0Gatsby v2Netlify CMS 2.x^3.0.0Gatsby v2netlify-cms-app 2.9.x^4.0.0即下文文档对应的版本破坏性变更其中^4.0.0是一个 breaking changeCMS 的依赖从旧的netlify-cms包切换为新发布的netlify-cms-app^2.9.x。当前仓库中该包的 package.json 版本已到7.13.0-next.0其 peerDependencies 要求gatsby^5.0.0-next、netlify-cms-app^2.9.0、webpack^5.0.0、react^18.0.0与 CHANGELOG 中最后一条 7.12.02023-08-24对应 Gatsby v5.12的记录相吻合。也就是说本文描述的参数体系适用于 Gatsby v5 时代7.x的最终实现。二、Overview插件到底做了什么原 README 的 Overview 一句话概括自动为站点生成一个admin/index.html并附带默认实现的 Netlify CMS。Netlify CMS 本身是一个 React 单页应用面向技术或非技术编辑用于通过 API 编辑基于 Git 的内容安装和配置都非常简单。从源码可以验证这条描述。插件的逻辑全部集中在 src/gatsby-node.js 中核心是onCreateWebpackConfig它在develop与build-javascript两个 stage 下基于 Gatsby 当前 webpack 配置派生出一个完全独立的第二份 webpack 配置entry 为插件自带的cms.js外加可选的cms-identity.js与用户自定义模块输出目录为output: { path: path.join(program.directory, public, publicPathClean), }即直接写进站点的public/{publicPath}目录publicPath默认admin首尾斜杠被trim掉最终随gatsby build产物一起部署。而 src/index.js 只有一行// noop注释——这个包的main入口什么都不做所有工作都由 Gatsby API 文件gatsby-node.js/gatsby-browser.js承担。这是理解该插件的关键结构特征它是一个纯构建期 少量客户端期的插件不参与 GQL 数据层。此外源码中还暴露了一个 README 未写的细节onPreInit会尝试require.resolve(netlify-cms)若发现用户安装了已废弃的旧包netlify-cms会打印警告提示改用netlify-cms-app见 src/gatsby-node.js#L54-L63。三、安装与快速上手原 README 的 Install 与 How to use 步骤完整保留如下这是可复制的最小接入流程。1. 安装依赖注意必须安装netlify-cms-app这是 4.0 的破坏性变更点npm install netlify-cms-app gatsby-plugin-netlify-cms2. 在gatsby-config.js中启用插件plugins: [gatsby-plugin-netlify-cms]3. 放置 Netlify CMS 的配置文件到static/admin/config.yml该文件由static目录原样复制到构建产物CMS 单页应用启动时读取它来声明后端、集合与字段。构建完成后访问站点根路径下的/admin/即可打开 CMS。开发模式下还有专门的体验优化插件实现了onCreateDevServer在 Gatsby dev server 上注册了/{publicPath}路由直接把public/{publicPath}/index.html通过res.sendFile吐给浏览器见 src/gatsby-node.js#L65-L78这样开发时不用刷新到/admin/index.html长路径同时FriendlyErrorsPlugin会在控制台显示“Netlify CMS is running at http(s)://{host}:{port}/{publicPath}/”的编译成功提示。四、完整参数参考逐项对照源码原 README 的 Options 章节列出了 8 个插件选项且全部可选。以下在完整继承原 README 说明的基础上结合onCreateWebpackConfig中解构出的默认值publicPath admin、htmlTitle Content Manager、htmlFavicon 、manualInit false、enableIdentityWidget true、includeRobots false见 src/gatsby-node.js#L80-L92逐一展开。modulePath可选string | Arraystring默认undefined如果你需要定制 Netlify CMS——例如注册自定义 widget、定制预览窗格样式——需要把这些代码写进一个 JS 模块并通过modulePath把路径交给 Gatsby。该模块及其整条 import 链导入的所有样式都会被插件自动应用到编辑器的预览窗格中。plugins: [ { resolve: gatsby-plugin-netlify-cms, options: { /** * 一种惯例是把 Netlify CMS 的定制代码放在 src/cms 目录中。 */ modulePath: ${__dirname}/src/cms/cms.js, }, }, ]原 README 给出的该模块示例代码完整保留/** * netlify-cms-app 的默认导出是一个对象包含所有 * Netlify CMS 扩展注册方法如 registerWidget 和 * registerPreviewTemplate。 */ import CMS from netlify-cms-app /** * 任何导入的样式都会自动应用到编辑器预览窗格 * 因此无需再对导入样式使用 registerPreviewStyle。 * 但如果在部署到 netlify 平台时遇到导入 css/sass/scss * 的构建错误可能需要按 netlify 官方文档的做法 * 使用 raw css registerPreviewStyle 的方式。 */ import module-that-imports-styles.js import styles.scss import ../other-styles.css /** * 假设你在独立文件中创建了自定义图片画廊 widget 与预览组件 */ import ImageGalleryWidget from ./image-gallery-widget.js import ImageGalleryPreview from ./image-gallery-preview.js /** * 注册导入的 widget */ CMS.registerWidget(image-gallery, ImageGalleryWidget, ImageGalleryPreview)源码印证了这条“样式自动生效”的链路modulePath被concat进 entry 数组entry.cms依次为cms.js、可选的cms-identity.js、再拼上modulePath数组并过滤空值见 src/gatsby-node.js#L135-L142插件自持一份MiniCssExtractPlugin({ filename: [name].css })实例把 entry 产物中的样式抽成cms.csssrc/cms.js 末尾调用CMS.registerPreviewStyle(cms.css)把这份 CSS 注册为预览窗格样式同时HtmlWebpackSkipAssetsPlugin会把cms.css从index.html的 link 中剔除——因为该 CSS 的目标是预览窗格而非 CMS 管理界面本身见 src/gatsby-node.js#L199-L202。manualInit可选boolean默认false设为true表示需要手动初始化 Netlify CMS。插件负责把window.CMS_MANUAL_INIT置为true通过 webpack 的plugins.define注入常量CMS_MANUAL_INIT见 src/gatsby-node.js#L204-L210。plugins: [ { resolve: gatsby-plugin-netlify-cms, options: { manualInit: true, }, }, ]原 README 给出的模块示例完整保留import CMS from netlify-cms-app /** * 可选传入一个 config 对象。 * 若存在 config.yml该对象会被合并进去。 */ CMS.init({ config: { backend: { name: git-gateway, }, }, })对应源码行为在 src/cms.js#L13-L20若CMS_MANUAL_INIT为真则跳过CMS.init()并打印提示把初始化时机交给你的modulePath模块。此外 src/cms.js 还会设置window.___emitter与window.___loader这两个 Gatsby 组件所需的全局变量——从源码注释可推断这是为了让 CMS 页面中可能用到的 Gatsby 组件如gatsby-link不依赖 Gatsby 完整运行时也能安全工作。enableIdentityWidget可选boolean默认true默认为true让 Netlify Identity 无需配置即可用于 CMS 登录。若不使用 Netlify Identity将其设为false可以减小 bundle 体积。plugins: [ { resolve: gatsby-plugin-netlify-cms, options: { enableIdentityWidget: true, }, }, ]从源码看这个开关影响四处externals 列表为真时在最前面插入netlify-identity-widget全局名netlifyIdentityUMD 文件build/netlify-identity.js见 src/gatsby-node.js#L123-L131entry 是否包含插件自带的 src/cms-identity.js为真时通过setWebpackConfig强制把netlify-identity-widget拆成独立 chunk生产构建的splitChunks.cacheGroups见 src/gatsby-node.js#L274-L290为假时直接注入IgnorePlugin将其从打包中排除src/gatsby-node.js#L291-L298src/gatsby-browser.js 的onInitialClientRender只有在开关为真且 URL hash 命中 token 路由时才动态import(netlify-identity-widget)——也就是说它把这部分代码做成了按需 chunk只有真正走到 Identity 回调时才加载。publicPath可选string默认admin定制 CMS 在 Gatsby 站点中的路径。源码中它会先被trim(publicPath, /)清理然后同时作用于三处输出目录public/{publicPath}、onCreateDevServer的app.get路由、以及plugins.define注入的CMS_PUBLIC_PATH常量src/cms-identity.js#L8 用它拼接登录后重定向地址${__PATH_PREFIX__}/${CMS_PUBLIC_PATH}/。htmlTitle可选string默认Content Manager定制 CMS HTML 中title标签的值即浏览器标签栏显示的名称。源码中直接传给HtmlWebpackPlugin({ title: htmlTitle, ... })。htmlFavicon可选string默认定制 CMS HTML 中favicon标签的值。同样传给HtmlWebpackPlugin的favicon参数。includeRobots可选boolean默认false默认 CMS 页面不会被爬虫索引。设为true会添加一个允许索引的meta标签。源码实现即meta: { robots: includeRobots ? all : none }src/gatsby-node.js#L190-L197所以生产环境应确认这个值符合你的安全预期——CMS 入口通常不应被搜索引擎收录。customizeWebpackConfig可选function用于定制这份专用 webpack 配置的函数接收两个参数configNetlify CMS 专用的 webpack 配置对象从 Gatsby 的onCreateWebpackConfigAPI 解构出的{ store, stage, pathPrefix, getConfig, rules, loaders, plugins }。原 README 示例完整保留plugins: [ { resolve: gatsby-plugin-netlify-cms, options: { customizeWebpackConfig: (config, { plugins }) { const Plugin require(...) config.plugins.push( plugins.define({ process.env.MY_VAR: JSON.stringify(my var value), }) ) config.plugins.push(new Plugin()) }, }, }, ]源码中该钩子在所有内置插件装配完成后、actions.setWebpackConfig之前被调用src/gatsby-node.js#L262-L272因此你 push 的插件/定义一定会生效。五、全参数示例原 README 的 Example 章节所有选项均非必需完整保留plugins: [ { resolve: gatsby-plugin-netlify-cms, options: { modulePath: path/to/custom/script.js, // default: undefined enableIdentityWidget: true, publicPath: admin, htmlTitle: Content Manager, htmlFavicon: path/to/favicon, includeRobots: false, }, }, ]六、彻底禁用 Identity Widget而不禁用 CMS原 README 的 Disable widget on site 章节如果你不在站点中使用 Netlify Identity可以选择完全禁用 widget而不是 CMS 本身。在gatsby-node.js中加入const webpack require(webpack) exports.onCreateWebpackConfig ({ actions }) { actions.setWebpackConfig({ plugins: [ new webpack.IgnorePlugin({ resourceRegExp: /^netlify-identity-widget$/, }), ], }) }这段针对的是站点自身的打包防止某处代码间接引入netlify-identity-widget而膨胀主 bundle与插件选项enableIdentityWidget: false针对的CMS 打包是两条独立路径——源码中插件在 CMS 配置里注入的 IgnorePluginsrc/gatsby-node.js#L291-L298用的正是同一个resourceRegExp。而站点侧的 src/gatsby-browser.js 解释了为什么 Identity 回调可能出现在普通页面上它监听onInitialClientRender当 URL hash 匹配(confirmation|invite|recovery|email_change)_token...、erroraccess_deniederror_description403或access_token时才动态导入 widget、完成 Identity 初始化并在登录成功后把用户重定向回${__PATH_PREFIX__}/${publicPath}/。理解了这一机制你就能解释“为什么登录成功后浏览器回到了 CMS 页面”这类现象。七、底层构建流程源码走读把onCreateWebpackConfig的关键步骤串起来可以看清这份“第二份站点”是怎么被独立构建的全部基于 src/gatsby-node.jsstage 过滤仅在develop/build-javascript下工作其他 stage 直接返回externals 声明L101-L121react、react-dom、netlify-cms-app以及开关为真时的netlify-identity-widget都以 UMD 全局变量形式外置不进 CMS 的 bundleCopyPlugin 拷贝 UMD 文件L212-L238从各包package.json所在目录定位react.production.min.js、react-dom.production.min.js、dist/netlify-cms-app.js及其 sourcemap复制到输出目录HtmlWebpackTagsPlugin再把这些脚本以append: false的方式注入index.html头部复用并清洗 Gatsby 的 webpack 配置通过自写的deepMap递归遍历module.rules用replaceRule剔除带babel-preset-gatsby/dependencies.js预设的 javascript rule该预设是 Gatsby 依赖链专用的CMS 不需要L31-L52同时过滤掉MiniCssExtractPlugin、GatsbyWebpackStatsExtractor、StaticQueryMapper、PartialHydrationPlugin这几个会干扰 CMS 编译的内置插件实例L154-L165define 注入四个常量__PATH_PREFIX__站点 pathPrefix、CMS_PUBLIC_PATH、CMS_MANUAL_INIT、PRODUCTIONstage ! develop它们分别是重定向拼接、手动初始化、生产/开发行为分支的依据资源优化生产构建保留 Gatsby 的 minimizerdevelop 阶段清空 minimizer源码注释说明这是为了避免生产构建时的 Node 内存溢出source map 在 develop 用cheap-module-source-map生产用source-mapL248-L254两种运行模式develop阶段调用webpack(config).watch(...)常驻监听所以开发时 CMS 变更会热重建build-javascript阶段则webpack(config).run(...)一次性编译且一旦有编译错误就 reject 使整个gatsby build失败L301-L314。依赖方面package.json 的 dependencies 恰好对应上述机制html-webpack-plugin^5.5.3生成 index.html、html-webpack-tags-plugin^3.0.2注入 UMD script 标签、html-webpack-skip-assets-plugin^1.0.3剔除cms.css、mini-css-extract-plugin1.6.2抽取cms.css、soda/friendly-errors-webpack-plugin1.8.1开发成功提示、copy-webpack-plugin^7.0.0拷贝 UMD 资产、netlify-identity-widget^1.9.2Identity 登录组件本体。八、迁移与适用范围迁移按 README 顶部的声明新项目或维护中的项目应改用gatsby-plugin-decap-cmsNetlify CMS 更名后的社区继任项目本插件不再演进仓库中的 CHANGELOG 也显示其自 7.12.0 之后仅跟随 monorepo 做依赖类变更。适用前提本插件的参数体系modulePath/manualInit/enableIdentityWidget/publicPath/htmlTitle/htmlFavicon/includeRobots/customizeWebpackConfig与 7.xGatsby v5的源码一致Gatsby v1/v2 老项目的 2.x/3.x/4.x 版本行为以对应 tag 的历史 README 为准参数默认值可能不同。与仓库其他文档的关系Netlify CMS 作为数据源的完整用法集合配置、后端选择等可参考仓库内的 sourcing-from-netlify-cms 与 headless-cms 文档它们与本文的构建层视角互补。【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考