ARTICLE DETAIL

资讯详情

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

使用 Webpack 与 Babel 构建自定义 GraphiQL:插件开发与工程化配置实战指南

使用 Webpack 与 Babel 构建自定义 GraphiQL:插件开发与工程化配置实战指南 使用 Webpack 与 Babel 构建自定义 GraphiQL插件开发与工程化配置实战指南【免费下载链接】graphiqlGraphiQL the GraphQL LSP Reference Ecosystem for building browser IDE tools.项目地址: https://gitcode.com/GitHub_Trending/gr/graphiql本篇指南基于仓库 examples/graphiql-webpack 示例工程完整演示如何用 webpack 5 Babel 7 从零搭建一个 ES6 语法的 GraphiQL 应用包括开发/生产构建配置、Webpack Dev Server 热更新、接入官方插件Explorer、Code Exporter、以及从零编写一个切换 GraphQL 服务地址的自定义插件。读完本篇你将掌握 GraphiQL 插件 API 的基本形态、graphiql/react上下文 Hook 的用法以及如何让 GraphiQL 在任意自定义打包管线中稳定运行。示例工程定位与整体结构这个示例的核心目的是验证并展示在 webpack 与 babel 的配合下可以直接在自己应用中转译并集成基于 ES6 的 GraphiQL 实现而不必依赖 CDN 或脚手架预设。除了基础渲染它还覆盖了两个进阶场景直接使用官方插件graphiql/plugin-explorer与graphiql/plugin-code-exporter编写自定义插件一个用于动态切换 GraphQL schema 服务地址的 Select Server 插件。工程还额外演示了 PWAWorkbox Service Worker 生成、Web App Manifest 生成、静态资源拷贝等生产级打包能力因此它既是一个最小可运行示例也是一份可裁剪的工程化模板。工程关键文件一览文件职责examples/graphiql-webpack/package.json依赖与脚本定义examples/graphiql-webpack/webpack.config.jswebpack 5 完整配置开发/生产examples/graphiql-webpack/babel.config.js复用仓库根级 Babel 配置examples/graphiql-webpack/index.html.ejsHTML 模板含 manifest 与图标examples/graphiql-webpack/src/index.jsx应用入口与 GraphiQL 组装examples/graphiql-webpack/src/select-server-plugin.jsx自定义插件完整实现examples/graphiql-webpack/src/snippets.jsCode Exporter 插件代码片段定义examples/graphiql-webpack/src/index.css主题样式导入与自定义样式快速启动与构建命令在 examples/graphiql-webpack 目录下执行即可yarn yarn startyarn安装依赖仓库为 yarn 工作区根目录 yarn.lock 统一锁版本yarn start实际等价于NODE_ENVdevelopment webpack-cli serve见 package.json会启动 webpack dev server 并提供热更新。工程还提供了两个生产相关脚本build: webpack-cli, build-demo: node ../../scripts/stage-demo.mts webpackbuild用于常规生产构建build-demo调用仓库 scripts/stage-demo.mts 将示例产物暂存为演示站点。需要指出的是webpack.config.js通过process.env.NODE_ENV区分环境直接运行yarn build时若不设置该变量mode会回退为developmentmode: process.env.NODE_ENV ?? development因此生产构建建议显式设置环境变量。Webpack 工程化配置深度解析开发/生产双模式入口与热更新配置通过两个环境变量驱动webpack.config.js 中isDev由NODE_ENV development决定isHMR由WEBPACK_SERVEwebpack-cli serve 自动注入决定。开发模式下入口会额外注入三段代码见 webpack.config.jsentry: isDev ? [ react-hot-loader/patch, // 激活 React HMR webpack-dev-server/client?http://localhost:8080, // dev server 客户端 webpack/hot/only-dev-server, // 仅对成功更新做热替换 ./src/index.jsx, // 应用入口 ] : ./src/index.jsx,生产构建则只保留./src/index.jsx一个入口。输出文件名带 contenthash 并启用clean保证发布时缓存策略与目录整洁output: { filename: [name].[contenthash].js, clean: true, }devServer 配置了hot: true并注释说明了allowedHosts的用途——当本地需要以local.test.com、graphiql.com等自定义域名访问以绕过 CORS 时可在/etc/hosts中将这些域名指向127.0.0.1同时在此白名单中登记见 webpack.config.js。loader 规则与 GraphiQL 依赖的特殊处理module.rules覆盖了.js/.jsx、.css、.svg、字体文件与.mjs五类资源见 webpack.config.jsJS/JSX经babel-loader处理presets 使用babel/preset-envmodules: false模块转换交由 webpack 处理与babel/preset-reactCSSstyle-loadercss-loader将 GraphiQL 及其插件的样式直接注入页面SVGsvg-inline-loader内联为行内图标字体file-loader处理 woff/woff2/eot/ttf/otfMJS这是集成 GraphiQL 时的关键规则。graphql 相关依赖中仍有部分模块以.mjsESM发布webpack 5 默认可能将其当作 ES module 静态分析导致警告或报错因此显式声明{ type: javascript/auto, test: /\.mjs$/, include: /node_modules/ }让 webpack 将其作为普通 JS 处理见 webpack.config.js。resolve.extensions配置为[.js, .json, .jsx, .css, .mjs]保证省略扩展名的导入可被正确解析。生产插件PWA、HTML 模板与 Manifest非 HMR 模式下会启用GenerateSWWorkbox将应用打包为离线可用的 PWA并通过maximumFileSizeToCacheInBytes将单文件缓存上限放宽到 20MB见 webpack.config.jsif (!isHMR) { prodPlugins.push( new GenerateSW({ maximumFileSizeToCacheInBytes: 1024 * 1024 * 20, }), ); }HtmlWebpackPlugin以 index.html.ejs 为模板生成入口页页面中声明了manifest.json与logo.svg图标CopyPlugin把public目录整体拷贝到输出目录WebpackManifestPlugin则生成一份名为 GraphiQL PWA 的 Web App Manifest含多尺寸 SVG 图标、主题色#D60590、display: standalone等见 webpack.config.js。根级 Babel 配置复用示例的 babel.config.js 只有一行module.exports require(../../resources/babel.config);即直接复用仓库根目录的 babel.config.js。根配置包含babel/preset-env、babel/preset-react、babel/preset-typescript以及 class-properties、optional-chaining、nullish-coalescing、transform-private-methods 等插件并通过ESM/CDN环境变量切换模块输出格式commonjs/ 保持 ESM /umd。这意味着示例虽以 JSX 编写但底层配置与仓库各 TypeScript 包保持同一套转译基线这也是它能覆盖 ES6 各类语法特性的原因。入口代码组装 GraphiQL 应用src/index.jsx 完整展示了入口 → fetcher → 插件 → 渲染的标准组装流程。首先导入全部依赖import regenerator-runtime/runtime.js; import React, { useState, useEffect, useMemo } from react; import { createRoot } from react-dom/client; import { GraphiQL } from graphiql; import { explorerPlugin } from graphiql/plugin-explorer; import { getSnippets } from ./snippets; import { codeExporterPlugin } from graphiql/plugin-code-exporter; import { createGraphiQLFetcher } from graphiql/toolkit; import { useGraphiQL } from graphiql/react; import { serverSelectPlugin, LAST_URL_KEY } from ./select-server-plugin; import graphiql/setup-workers/webpack; import ./index.css;其中import graphiql/setup-workers/webpack;是 GraphiQL 在 webpack 环境下运行的关键一步——该入口在 packages/graphiql/src/setup-workers/webpack.ts 中仅一行import graphiql/react/setup-workers/webpack;其作用是以 webpack 兼容的方式加载 GraphQL language service 所需的 Web Worker。不同打包器对应不同的 setup-workers 入口Vite 对应graphiql/setup-workers/viteCDN 场景对应graphiql/setup-workers/esm.sh见 examples/graphiql-cdn/index.html 与 examples/graphiql-vite-react-router/app/routes/_index/graphiql.client.tsx 的用法。组件内部通过useMemo按当前 URL 重建三样东西见 src/index.jsxconst exporter useMemo( () codeExporterPlugin({ snippets: getSnippets({ serverUrl: currentUrl }) }), [currentUrl], ); const fetcher useMemo( () createGraphiQLFetcher({ url: currentUrl }), [currentUrl], ); const serverSelect useMemo( () serverSelectPlugin({ url: currentUrl, setUrl }), [currentUrl], ); return ( GraphiQL style{style} plugins{[serverSelect, explorer, exporter]} fetcher{fetcher} shouldPersistHeaders GraphiQLContextBound setUrl{setUrl} / /GraphiQL );要点explorerPlugin()在组件生命周期外实例化——除非插件需要接收 React 应用中的动态值此时才应放入useMemo源码注释对此有明确说明见 src/index.jsxGraphiQL内部由graphiql/react提供 context provider 树子组件GraphiQLContextBound通过useGraphiQL读取全局状态并从 storage 中恢复上次使用的服务地址再回调setUrl完成初始化见 src/index.jsxfunction GraphiQLContextBound({ setUrl }) { const storage useGraphiQL(state state.storage); const lastUrl storage.get(LAST_URL_KEY) ?? STARTING_URL; useEffect(() { setUrl(lastUrl); }, [lastUrl, setUrl]); return null; }页面默认指向公开的示例服务https://countries.trevorblades.com见 src/index.jsx启动后可直接发送查询代码中还保留了手写 fetcher的参考实现被注释的fetcher函数见 src/index.jsx展示不依赖graphiql/toolkit时如何用原生fetch组合请求Accept/Content-Type头、credentials: same-origin、JSON 解析失败回退到text()。实际运行使用的是createGraphiQLFetcher它在 packages/graphiql-toolkit/src/create-fetcher/createFetcher.ts 中实现除了url之外还支持headers、wsClient、fetch等选项。此外入口在页面加载后注册 Service Worker见 src/index.jsxif (serviceWorker in navigator) { window.addEventListener(load, () { navigator.serviceWorker .register(/service-worker.js) .then(registration console.log(SW registered:, registration)) .catch(error console.error(SW registration failed:, error)); }); }/service-worker.js正是 WorkboxGenerateSW在生产构建时生成的产物。自定义插件实战Select Server 插件这是示例中演示如何创建自定义插件的核心部分实现在 src/select-server-plugin.jsx 中。插件对象的结构非常直观见 select-server-plugin.jsxexport function serverSelectPlugin({ url, setUrl }) { return { title: Select Server, icon: () (svg.../svg), // 工具栏图标 content() { return SelectServer url{url} setUrl{setUrl} /; // 侧边栏面板内容 }, }; }即一个 GraphiQL 插件就是一个包含title侧边栏标题、icon工具栏图标与content面板 React 组件三个字段的对象。icon使用手写的 SVG 路径这与仓库 packages/graphiql-react/src/icons 中官方图标的组织方式一致——图标以独立 SVG 文件存放并通过index.tsx统一导出。面板组件SelectServer的关键在于通过useGraphiQL拿到全局状态const { storage, schema, isIntrospecting, fetchError } useGraphiQL( state ({ storage: state.storage, schema: state.schema, isIntrospecting: state.isIntrospecting, fetchError: state.fetchError, }), );storage持久化存储用于读写lastURLLAST_URL_KEY与previousURLsPREV_URLS_KEY两个键实现记住上次地址 历史地址列表schema/isIntrospecting/fetchError分别代表当前 schema、是否正在 introspection、抓取 schema 失败的错误信息插件据此在面板中渲染Schema retrieved successfully / Schema loading... / 错误详情三种状态见 select-server-plugin.jsx。切换地址的核心逻辑是点击按钮后校验输入以http开头 →setUrl(value)更新 fetcher →storage.set(LAST_URL_KEY, value)持久化 → 将新地址追加进历史列表并同步存储见 select-server-plugin.jsx。历史列表每一项都支持点击切换与删除实现最近使用服务的快速入口。自定义样式全部集中在 src/index.css 的select-server--*类名下并大量使用 GraphiQL 主题 CSS 变量如--color-primary、--color-base、--color-error、--color-success、--color-warning、--alpha-secondary等保证与 GraphiQL 明暗主题自动适配。从源码结构看这套插件 API 与官方插件Explorer、Code Exporter、History的形态一致它们同样以titleiconcontent结构导出对应graphiql/plugin-explorer、graphiql/plugin-code-exporter、graphiql/plugin-history三个包因此学会了本例就能阅读并定制任意官方插件的源码。插件示例二Code Exporter 与代码片段生成graphiql/plugin-code-exporter允许把当前查询导出为多种编程语言的代码。示例在 src/snippets.js 中定义了三个导出模板cURLlanguage: shellcodeMirrorMode: shell把查询拼进curl -X POST命令携带Content-Type: application/json请求头并将serverUrl即当前选中的服务地址作为请求终点Example Onelanguage: JavaScript输出export const query graphql\...Example Twolanguage: JavaScript额外引入import { graphql } from graphql。每个模板对象的结构为{ name, language, codeMirrorMode, options, generate(arg) }generate接收{ operationDataList }从中取出operationDataList[0].query做二次加工。例如 cURL 模板会把查询中的换行替换为空格以适配单行 shell 命令const exampleSnippetZero { name: cURL, language: shell, codeMirrorMode: shell, options: [], generate: arg curl -g \ -X POST \ -H Content-Type: application/json \ -d {query: ${arg.operationDataList[0].query.replaceAll(\n, )}} \ ${serverUrl}, };getSnippets({ serverUrl })返回模板数组在 src/index.jsx 中传给codeExporterPlugin({ snippets })由于依赖当前 URL它在useMemo中随currentUrl重建。snippets.js中的removeQueryName与getQuery工具函数则负责把匿名查询规范化为带query关键字的缩进格式方便嵌入 JS 模板字符串。若需要更多导出格式如 Python、TypeScript 等只需按同样结构追加模板对象即可。样式集成与主题适配src/index.css 通过import依次引入基础样式、两个官方插件样式与自定义插件样式import graphiql/style.css; import graphiql/plugin-explorer/style.css; import graphiql/plugin-code-exporter/style.css; import ./select-server-plugin.css;body背景使用hsl(var(--color-base))、#root高度设为100vh让 GraphiQL 编辑器铺满整个视口。这套基于 CSS 变量的主题体系来自 packages/graphiql-react/src/style/root.css定义--color-primary、--color-error、--color-success等基础令牌意味着自定义插件只需引用同一组变量就能在明/暗主题下保持一致观感无需为插件单独做主题适配。与仓库其他示例的横向对比本示例与仓库其他 GraphiQL 集成示例各有侧重可互为参考examples/graphiql-viteVite React 的轻量示例无 routerexamples/graphiql-vite-react-routerVite React Router 路由集成的现代示例examples/graphiql-nextjsNext.js App Router 集成examples/graphiql-cdn零构建、直接通过 ESM 从 esm.sh 加载的 CDN 方案使用graphiql/setup-workers/esm.shexamples/monaco-graphql-webpack基于 monaco-graphql 的 Monaco 编辑器方案。各示例统一通过import graphiql/setup-workers/bundler按打包器加载 Worker 支持这与本示例的 webpack 入口一一对应。此外graphiql-create-react-app示例已经移除以避免维护成本其 README 明确推荐改用 examples/graphiql-vite 或 examples/graphiql-nextjs见 examples/graphiql-create-react-app/README.md并提到 create-react-app 本身支持示例所需的全部语言特性——这从侧面说明只要构建管线具备 ES6 转译能力GraphiQL 的接入路径是通用的。常见问题与排查建议启动后编辑器无语法高亮/补全确认已import graphiql/setup-workers/webpack且.mjs规则存在否则 GraphQL language service 的 worker 无法在 webpack 下正确加载。自定义插件不显示确认插件对象包含title与content且被传入GraphiQL plugins{[...]}若插件需要读取全局状态必须在GraphiQL的 provider 树内渲染组件如本例中content返回的面板组件。切换服务地址后 schema 未更新fetcher 必须随 URL 变化重建本示例通过useMemo(() createGraphiQLFetcher({ url: currentUrl }), [currentUrl])实现直接修改 fetcher 内部闭包变量不会触发重建。生产构建产物含大量 hash 文件这是[name].[contenthash].js的预期行为配合GenerateSW与clean: true使用发布时直接托管dist目录即可。小结通过 examples/graphiql-webpack 这份工程化示例你可以完整掌握四条主线一是用 webpack 5 Babel 将 GraphiQL 及其语言服务 worker 正确接入自有构建管线二是用 dev server、热更新、PWA 与 manifest 搭建生产级 GraphiQL 站点三是理解并复用 GraphiQL 插件 APItitleiconcontent与graphiql/react的useGraphiQL上下文 Hook接入 Explorer、Code Exporter 等官方插件四是参考 Select Server 插件把切换 GraphQL 服务地址 历史记忆这类产品能力以插件形式优雅落地并利用 CSS 变量实现主题自适应。无论最终选择 webpack、Vite 还是 Next.js这份示例所展示的组装方式与插件机制都可以直接迁移到你的项目中。【免费下载链接】graphiqlGraphiQL the GraphQL LSP Reference Ecosystem for building browser IDE tools.项目地址: https://gitcode.com/GitHub_Trending/gr/graphiql创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表