Refine Material UI Inferencer 组件实战:用 `@refinedev/inferencer/mui` 自动生成 List / Show / Create / Edit 视图
2026/9/13 13:18:41 网站建设 项目流程

Refine Material UI Inferencer 组件实战:用@refinedev/inferencer/mui自动生成 List / Show / Create / Edit 视图

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

导读

本文聚焦 Refine v5 生态中的@refinedev/inferencer包及其 Material UI(MUI)适配层@refinedev/inferencer/mui,系统讲解如何在 Refine 应用中通过一行组件自动生成资源的 List、Show、Create、Edit 四类视图,并基于当前仓库源码深入剖析其"数据抓取 → 字段推断 → 代码生成 → 实时渲染"的底层工作原理。读完本文,你将掌握 Inferencer 的两种接入方式(resources路由推断与自定义组件显式传参)、四类视图的生成逻辑与字段类型映射,以及fieldTransformermeta等高级定制手段,从而在开发阶段快速产出可复制、可二次定制的 MUI 管理界面骨架。


一、Inferencer 是什么:基于数据结构的视图自动生成器

@refinedev/inferencer是 Refine 生态中负责"根据资源数据结构自动生成视图"的包。核心思路是:先向dataProvider发起请求拿到真实数据,再从数据结构推断每个字段的类型,最后生成并实时渲染一套可复制的代码。其目标是大幅缩短为资源编写视图的时间——生成的代码天然可编辑、可替换,开发者只需在此基础上微调即可上线。

该包按 UI 框架分包导出组件,本文的主角@refinedev/inferencer/mui即 Material UI 作用域,导出了四个单视图组件与一个聚合组件:

  • MuiListInferencer:列表视图
  • MuiShowInferencer:详情视图
  • MuiEditInferencer:编辑视图
  • MuiCreateInferencer:创建视图
  • MuiInferencer:聚合组件,根据当前路由的action自动切换到上面四个视图之一

这一点可以直接在仓库的包入口得到印证:packages/inferencer/src/inferencers/mui/index.tsx 中,MuiInferencer通过useParsed()拿到当前路由解析出的actionid,然后以switch分发到对应视图:

const { action, id } = useParsed(); switch (actionFromProps ?? action) { case "show": return <ShowInferencer {...props} id={idFromProps ?? id} />; case "create": return <CreateInferencer {...props} id={idFromProps ?? id} />; case "edit": return <EditInferencer {...props} id={idFromProps ?? id} />; default: return <ListInferencer {...props} id={idFromProps ?? id} />; }

同时该文件还导出了MuiListRendererMuiShowRendererMuiEditRendererMuiCreateRenderer四个渲染器,它们是各视图的代码生成核心(源码中以renderer as MuiListRenderer等形式 re-export)。

注意:Inferencer 组件是实验性(@experimental)功能,官方定位是开发期脚手架工具,不建议在生产环境使用。生成代码后应将其复制到业务代码中再定制。


二、快速接入:两种使用方式

Inferencer 组件可以从@refinedev/inferencer/mui直接导入,并且可以不传任何 props 直接放进路由——只要应用配置了routerProvider,组件就会从当前路由自动推断出resourceactionid

2.1 方式一:在resources路由中直接使用

在路由中放置<MuiInferencer />,路由路径本身(如/samples)即提供了资源名与动作信息:

import { ThemedLayout, RefineThemes, RefineSnackbarProvider, } from "@refinedev/mui"; import { CssBaseline, GlobalStyles } from "@mui/material"; import { ThemeProvider } from "@mui/material/styles"; import { BrowserRouter, Routes, Route, Outlet } from "react-router"; import { MuiInferencer } from "@refinedev/inferencer/mui"; const App = () => { return ( <BrowserRouter> <ThemeProvider theme={RefineThemes.Blue}> <CssBaseline /> <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} /> <RefineSnackbarProvider> <Refine dataProvider={dataProvider(API_URL)} routerProvider={routerProvider} resources={[ { name: "samples", list: "/samples", }, ]} > <Routes> <Route element={ <ThemedLayout> <Outlet /> </ThemedLayout> } > <Route path="/samples" element={<MuiInferencer />} /> </Route> </Routes> </Refine> </RefineSnackbarProvider> </ThemeProvider> </BrowserRouter> ); };

2.2 方式二:在自定义组件中显式传参

如果不依赖路由(例如资源与路由路径不完全一致,或需要放在自定义页面中),可以给组件显式传入resourceactionid三个 props:

import { MuiInferencer } from "@refinedev/inferencer/mui"; const SampleList = () => { return <MuiInferencer resource="samples" action="list" />; }; const SampleShow = () => { return <MuiInferencer resource="samples" action="show" id="1" />; }; const SampleCreate = () => { return <MuiInferencer resource="samples" action="create" />; }; const SampleEdit = () => { return <MuiInferencer resource="samples" action="edit" id="1" />; };

从类型定义看(packages/inferencer/src/types/index.ts),Inferencer 组件完整 props 包括:

Prop类型说明
resource/namestring要推断的资源名,二者任选其一
action"list" \| "show" \| "edit" \| "create"推断的动作类型,默认"list"
idstring \| number记录 ID,show/edit动作必用
fieldTransformer(field) => InferField \| undefined \| null \| false逐字段改写推断结果,返回空值时该字段被移除
meta嵌套对象按资源 × 方法传递getList/getMany/getOne/update/create/default的 meta 值,常用于 GraphQL 后端
hideCodeViewerInProductionboolean生产模式下隐藏代码查看器与提示块

2.3 在真实示例项目中的用法

仓库自带的可运行示例 examples/inferencer-material-ui 展示了完整落地方式。其页面文件非常精简,例如 examples/inferencer-material-ui/src/pages/blog-posts/list.tsx 只包含三行:

import { MuiListInferencer } from "@refinedev/inferencer/mui"; export const BlogPostList = () => { return <MuiListInferencer />; };

在 examples/inferencer-material-ui/src/App.tsx 中,blog_postscategories两个资源都注册了list/create/edit/show四条路由,并配合authProvideri18nProvider(react-i18next)、RefineKbarUnsavedChangesNotifier等标准能力,构成一个可直接npm install && npm run dev体验的完整 Demo。


三、四个视图分别如何生成

四个视图虽然都由数据驱动,但底层的 UI 原语、Hook 组合与数据来源各不相同。下面逐一对齐官方文档描述与源码实现。

3.1 List:基于useDataGrid的列表页

生成逻辑:按照 List API 响应生成示例列表视图,使用@refinedev/muiList组件与useDataGridHook。

对应实现位于 packages/inferencer/src/inferencers/mui/list.tsx。其 renderer 生成的组件骨架如下:

export const SampleList = () => { const { dataGridProps } = useDataGrid(); const columns = React.useMemo<GridColDef[]>(() => [ // 每个字段按推断类型生成一列…… ], []); return ( <List> <DataGrid {...dataGridProps} columns={columns} autoHeight /> </List> ); };

关键点:

  • 使用@mui/x-data-gridDataGrid承载数据,useDataGrid负责分页、排序、筛选等dataProvider.getList数据流;
  • 每个字段会按其推断类型生成对应的GridColDef,例如:
    • text/number→ 基础列;ID 列固定minWidth: 50且不参与flex伸缩,其余文本列minWidth: 200
    • emailEmailField渲染;urlUrlField渲染;
    • image→ 内联<img>缩略图(高度 50px、最大宽度 100px),多值场景逐个渲染;
    • dateDateFieldboolean→ MUICheckboxrichtextMarkdownField且截取前 80 字符;
    • relation(一对多/多对多)→ 通过useMany拉取关联资源,单值显示关联记录的展示字段,多值用多个TagField渲染。
  • 若资源可编辑/可查看/可删除,会自动追加actions列(EditButton/ShowButton/DeleteButton,均hideText图标模式);判定依据是资源的edit/show属性或meta.canEdit/meta.canShow/meta.canDelete
  • 当资源主键不叫id时,会自动生成getRowId={(row) => row?.<首字段>},保证 DataGrid 行键正确。

3.2 Show:基于useShow的详情页

生成逻辑:按照 API 响应生成示例详情视图,使用@refinedev/muiShow与各类字段组件(TextFieldComponentEmailFieldUrlFieldBooleanFieldDateFieldMarkdownFieldNumberFieldTagField),配合@refinedev/coreuseShow获取单条记录。

对应实现位于 packages/inferencer/src/inferencers/mui/show.tsx。其 imports 起始即包含:

import { useShow } from "@refinedev/core"; import { Show, TagField, TextFieldComponent, EmailField, UrlField, BooleanField, DateField, MarkdownField, NumberField, } from "@refinedev/mui"; import Typography from "@mui/material/Typography"; import Stack from "@mui/material/Stack";

关系字段的处理分两种情况:多值关系走useMany,单值关系走useOne,并分别绑定各自的加载态变量(xxxIsLoading)做 Loading 兜底。

3.3 Create:基于useForm的创建页

生成逻辑:根据列表 API 响应中的第一条记录生成示例创建视图,使用@refinedev/muiCreate组件与@refinedev/react-hook-formuseFormHook。

对应实现位于 packages/inferencer/src/inferencers/mui/create.tsx。生成的组件骨架:

export const SampleCreate = () => { const { saveButtonProps, refineCore: { formLoading }, register, control, formState: { errors }, } = useForm(); return ( <Create isLoading={formLoading} saveButtonProps={saveButtonProps}> <Box component="form" sx={{ display: "flex", flexDirection: "column" }} autoComplete="off"> {/* 各字段输入控件…… */} </Box> </Create> ); };

字段控件映射规则:

  • text/number/email/url/richtext→ MUITextField,其中number会附加valueAsNumber: truerichtext会附加multiline,日期字段因@refinedev/mui未内置 DatePicker 而以注释形式提示参照 MUI 官方文档自行扩展;
  • booleanCheckbox+FormControlLabel,通过react-hook-formController接入表单;
  • relationuseAutocomplete+ MUIAutocomplete,支持multiple(多选)模式,getOptionLabel使用关系资源的展示字段(默认取title,或对象推断出的展示键),并附加required校验;
  • 所有文本类字段默认带required: "This field is required"校验与错误提示(errors驱动error/helperText);
  • ID 字段(isIDKey判定)在创建页被直接省略。

3.4 Edit:基于useForm的编辑页

生成逻辑:按照 API 响应生成示例编辑视图,使用@refinedev/muiEdit组件与@refinedev/react-hook-formuseFormHook。

对应实现位于 packages/inferencer/src/inferencers/mui/edit.tsx。它复用了与 Create 几乎一致的字段控件生成逻辑(TextField/Checkbox/Autocomplete/Controller),区别在于:

  • 使用Edit布局替代Create布局,并依赖id定位记录;
  • useForm通过refineCoreProps注入action: "edit"与资源信息,加载既有数据回填表单;
  • 关系字段同样使用useAutocomplete提供选项列表。

四、代码生成与实时渲染:createInferencer 的完整流水线

所有视图组件都是通过 packages/inferencer/src/create-inferencer/index.tsx 中的createInferencer工厂函数构建的。整个推断流程可以概括为五步:

  1. 取数useInferFetch按动作类型取数——edit/showresource + id请求单条记录;list/create发列表请求并取其中一条作为推断样本(见 packages/inferencer/src/use-infer-fetch/index.tsx)。
  2. 字段推断composeInferencers串联 12 个内置字段推断器(arraybooleandateemailimagenullishnumberobjectrelationrichtexttexturl,见 packages/inferencer/src/field-inferencers/index.ts),逐字段判定类型。
  3. 字段变换composeTransformers串联 4 个内置变换器(imageByKeyrelationByResourcerelationToFieldablebasicToRelation,见 packages/inferencer/src/field-transformers/index.ts),把原始推断结果映射到资源体系上(如把*_id字段关联到具体 resource)。
  4. 关系补充useRelationFetch对识别出的关系字段额外拉取关联数据,供预览渲染使用。
  5. 生成 + 渲染:调用该 UI 包对应动作的renderer函数返回代码字符串;同一份字符串既作为react-live的实时渲染源码,也展示在代码查看器中供用户复制(prepareLiveCode负责注入 scope,removeHiddenCode负责清理 live 演示用的隐藏代码)。
const Inferencer = ({ resourceName, fieldTransformer, hideCodeViewerInProduction, meta, id, }) => { const { resource, resources } = useResourceParams({ resource: resourceName }); const { data: record, datas: records, loading: recordLoading, error: inferError, } = useInferFetch(type, resourceName ?? resource?.name, id, meta); // ...字段推断与变换... const code = renderer({ resource, resources, fields: clearedFields, infer, meta, isCustomPage: resource.name !== resourceFromURL?.name, id, i18n, }); return ( <> {(recordLoading || relationLoading) && <LoadingComponent />} {!recordLoading && !relationLoading && ( <> <LiveComponent code={prepareLiveCode(code, componentName(...))} ... /> {!hiddenCodeViewer && <CodeViewerComponent code={removeHiddenCode(code)} />} </> )} </> ); };

组件命名规则:组件名 = 资源展示名(或 resource.name)+ 动作名,例如资源categories+ 动作listCategoryList;若资源定义了option.label则优先使用。

在 MUI 作用域,additionalScope注入了@refinedev/muiuseDataGridListEditButtonDeleteButton等)、@mui/x-data-gridDataGrid)、@mui/materialCheckbox等)的模块对象,使生成代码中的这些符号能在 live 环境中直接求值(见 packages/inferencer/src/inferencers/mui/list.tsx)。加载态与错误态组件分别位于 loading.tsx(居中CircularProgress)与 error.tsx。

4.1 字段类型推断的底层规则

InferField类型(packages/inferencer/src/types/index.ts)包含keytyperelationmultiplefieldableaccessorresourcepriorityrelationInfer等字段。推断的关键机制:

  • 优先级机制:推断器可返回priority数值,数值越大表示类型越精确。例如created_at字段既是date也是text,但date推断器命中时优先级更高(见 packages/inferencer/src/field-inferencers/date.ts:匹配_at/_on/At/On等后缀且dayjs校验通过,返回priority: 1),从而胜出。
  • 数组与对象递归推断:数组字段记录为array类型,并对其元素值递归执行同一套推断;对象字段则尝试挑选一个"展示键"(如labeltitlenameusernameurl等 PresentationalKeys),被选中后fieldable: true,可被当作普通标量字段参与渲染。
  • 多记录投票list/create场景会对多条记录逐条推断,再对每个 key 统计出现最多的type作为最终结论(多数决),并据此构造一条"最常见记录"作为渲染样本(见 create-inferencer/index.tsx)。

4.2 关系字段如何被识别

判定一个字段是否为relation遵循以下条件(见 documentation/docs/packages/inferencer/index.md):

  • 属性名以id/ids结尾(支持 camelCase、PascalCase、snake_case、kebab-case、UPPER_CASE 等写法,可带[]数组后缀);
  • 属性是仅含单个id键的对象;
  • 属性是"仅含单个id键的对象数组"或 UUID 兼容的字符串/数字数组;
  • 属性为字符串/数字,且属性名与已知资源(单数或复数)匹配。

确定关联资源时,先尝试在resources中按属性名单复数匹配;若找不到,则向defaultdataProvider 分别发单数、复数(剥离id后缀)两次探测请求,任一返回 200 即认定为关系;全部失败则撤销relation标记,按普通字段处理。若后端关系无法自动识别,可通过fieldTransformer手动修正。

4.3 通过meta适配 GraphQL 后端

Inferencer 支持用嵌套meta一次性为"多个资源 × 多个方法"提供元数据,语法如下:

<MuiListInferencer meta={{ posts: { getList: { fields: ["id", "title", "content", "category_id", "created_at"], }, }, categories: { default: { fields: ["id", "title"], }, }, }} />

其中default是该资源所有方法的兜底值。渲染器内部通过getMetaProps将对应资源/方法的 meta 注入生成的useDataGriduseManyuseOneuseForm等调用;当检测到meta中存在gqlQuery/gqlMutation时,还会自动为生成代码加入graphql-taggql导入(见各视图 renderer 开头的hasGql分支)。

4.4 用fieldTransformer修改推断字段

fieldTransformer接收每个推断出的InferField,返回修改后的字段;返回undefined/null/false则从预览与生成代码中移除该字段:

<MuiListInferencer fieldTransformer={(field) => { if (field.key === "secret_field") { return false; // 移除敏感字段 } if (field.key === "category" && field.type === "object") { return { ...field, accessor: "label" }; // 修改对象字段的取值路径 } return field; }} />

五、开发期体验与生产环境约束

5.1 开发期体验

  • 页面加载时先显示居中CircularProgress加载态,取数与关系推断完成后渲染实时预览;
  • 预览下方带有代码查看器(SharedCodeViewer),展示即将复制到项目中的完整组件代码,可直接复制粘贴;
  • 若取数失败(如资源路径错误、dataProvider 未配置),渲染ErrorComponent提示错误信息。

5.2 生产环境约束

  • 官方明确:Inferencer 组件仅用于开发环境,不应在生产环境使用(见 documentation/docs/packages/inferencer/index.md);
  • 生产模式下若设置hideCodeViewerInProduction={true},代码查看器与提示块会被隐藏(process.env.NODE_ENV !== "development" && hideCodeViewerInProduction判定,见 create-inferencer/index.tsx);
  • 推荐工作流:开发期用 Inferencer 快速生成视图代码 → 复制到pages/下的业务组件 → 按需求二次定制(调整列宽、字段顺序、校验规则、自定义按钮等)→ 替换路由中的 Inferencer 组件。

六、示例与延伸阅读

  • 可运行示例:examples/inferencer-material-ui(npm installnpm run dev即可体验,含blog_postscategories两个资源及登录、i18n、kbar 等完整配置);
  • 包级集成文档:documentation/docs/packages/inferencer/index.md(安装方式、推断规则、meta 语法、fieldTransformer 用法);
  • MUI 作用域实现源码:packages/inferencer/src/inferencers/mui(index.tsxlist.tsxshow.tsxcreate.tsxedit.tsxerror.tsxloading.tsxcode-viewer.tsx);
  • 通用推断内核:packages/inferencer/src/create-inferencer/index.tsx、packages/inferencer/src/field-inferencers、packages/inferencer/src/field-transformers;
  • 其他 UI 作用域:Ant Design、Mantine、Chakra UI、Headless 的 Inferencer 组件位于 packages/inferencer/src/inferencers 目录下,接入方式与 MUI 完全一致(导入路径对应改为@refinedev/inferencer/antd等),文档见 documentation/docs/ui-integrations/ant-design/components/inferencer、documentation/docs/ui-integrations/mantine/components/inferencer、documentation/docs/ui-integrations/chakra-ui/components/inferencer。

提示:本文所有源码引用均来自当前仓库的packages/inferencerexamples/inferencer-material-uidocumentation/docs目录,生成代码的细节以你所安装的@refinedev/inferencer版本为准;由于该包仍处于实验阶段,接口可能随版本演进而变化。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询