尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Refine 通用概念指南:Headless 架构、Provider 体系与 Hook 驱动的状态管理

Refine 通用概念指南:Headless 架构、Provider 体系与 Hook 驱动的状态管理 Refine 通用概念指南Headless 架构、Provider 体系与 Hook 驱动的状态管理【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本篇指南以 Refine v5 官方文档的General Concepts章节documentation/docs/guides-concepts/general-concepts/index.md为骨架结合仓库内packages/core的真实源码与上下文实现系统讲解 Refine 的 Headless 设计、Resource、Provider、Hook、meta与状态管理六大核心概念。读完本文你将理解 Refine 如何通过可插拔的 Provider 与统一的 Headless Hooks 构建内部工具、管理后台与 B2B 应用并能独立编写自己的 Data / Auth / Access Control 等 Provider掌握查询缓存、失效与乐观更新的底层机制。Headless 概念业务逻辑与 UI 彻底解耦Refine 的核心设计理念是Headless无头。它不是一个开箱即用的样式组件库而是提供了一整套hooks、components与providers的集合。由于业务逻辑与 UI 完全解耦开发者可以不受任何约束地定制界面可以配合 TailwindCSS 等流行 CSS 框架或完全从零编写自己的样式也可以直接使用官方提供的Ant Design、Material UI、Mantine、Chakra UI四种 UI 集成这些库本质上是与无头核心包refinedev/core深度集成的组件集合帮助你快速起步。这种架构带来的直接收益是无论你选择哪种 UI 库甚至是自研设计体系数据获取、权限控制、i18n 等业务能力都保持不变——这正是 Hook 概念中统一接口的基础。Resource 概念应用的实体抽象在 Refine 中resource资源是一个中心概念代表一个entity实体将应用的各个层面串联起来。它通常指一个数据实体如products、blogPosts、orders。通过 resource 定义你可以用结构化的方式管理应用将复杂操作通过各类providers与UI 集成抽象为更简单的动作。一个典型的 resource 定义如下import { Refine } from refinedev/core; export const App () { return ( Refine resources{[ { name: products, list: /my-products, show: /my-products/:id, edit: /my-products/:id/edit, create: /my-products/new, }, ]} {/* ... */} /Refine ); };name是资源的标识符例如对应后端的表名或集合名list、show、edit、create则将该资源映射到具体的路由。UI 集成的侧边栏菜单、面包屑、CRUD 按钮都会依据这些定义自动生成。Provider 概念可插拔的构建块Provider 是 Refine 的构建块用于管理应用的不同方面如数据获取、路由、访问控制等。它们是可插拔pluggable的——既可以使用内置 Provider也可以创建自己的实现从而按需定制应用行为。在源码层面这些 Provider 全部作为可选属性挂载在Refine /组件上见 packages/core/src/contexts/refine/types.ts其中声明了dataProvider、authProvider、liveProvider、notificationProvider、accessControlProvider、auditLogProvider、i18nProvider、routerProvider等字段并在 packages/core/src/contexts/refine/index.tsx 中向下层 Context 传递。各 Provider 职责总览Provider职责Data Provider与后端数据源通信处理获取、创建、更新、删除记录以及缓存与失效Authentication Provider管理用户认证与授权流程处理重定向与错误场景Access Control Provider处理授权与访问控制用于隐藏/禁用按钮和菜单项或保护路由与组件Notification Provider启用通知功能如在操作成功或出错后展示通知I18n Provider启用国际化渲染翻译后的菜单项、按钮文本、表格列、页面标题等Live Provider启用实时更新例如某用户创建新记录后其他用户的列表页无需刷新即可看到Router Provider将路由匹配到资源支持面包屑、CRUD 操作后的自动重定向、渲染菜单项等导航能力Audit Log Provider为 CRUD 操作发送审计日志Hook 概念统一且无头的接口Refine 采用Hook 驱动的架构这是 React 开发中现代且高效的模式显著提升了开发体验与应用性能。所有 Hooks 都是headless的它们与具体库无关为你的需求提供统一接口无论你选择哪种路由或 UI 库。最典型的例子是路由Refine 针对React Router、Next.js、Remix、Expo提供了不同的内置 router provider但导出自refinedev/core的单个useGoHook却可以在任何路由方案下导航到指定资源的页面实现见 packages/core/src/hooks/router/use-go/index.tsx。同样地无论你使用 Casbin 还是 Cerbos 做授权都有统一的useCanHook 控制组件访问权限无论你偏好next-i18next还是react-i18next都有统一的useTranslateHook 处理翻译。数据获取、认证、访问控制、通知、i18n 等领域都遵循这一一套 Hook、多种实现的模式。Provider 详解从接口到源码Data ProviderData Provider 是前端与后端数据源之间的桥梁负责所有数据相关操作获取、缓存、创建、更新、删除。每个数据操作通常关联特定 resource——例如获取products资源的数据时Data Provider 知道该请求哪个端点、如何处理响应import { DataProvider } from refinedev/core; const myDataProvider: DataProvider { getOne: async ({ resource, id }) { const response await fetch( https://example.com/api/v1/${resource}/${id}, ); const data await response.json(); return { data }; }, // other methods... };Refine 为 REST、Strapi、AirTable、Supabase、GraphQL 等主流数据源提供了多种内置 Data Provider完整列表见 Data Providers 文档。Hooks组件中可使用useList、useOne、useCreate、useEdit、useShow等 Hooks 获取数据。以useOne为例实现位于 packages/core/src/hooks/data/useOne.tsuseList见 packages/core/src/hooks/data/useList.tsimport { useOne } from refinedev/core; export const MyPage () { const { result, query: { isLoading }, } useOne({ resource: products, id: 1 }); if (isLoading) { return Loading.../; } return {result?.name}/; };注意返回值同时暴露了result数据结果与queryTanStack Query 对象含isLoading等状态这正是 Refine 将 React Query 深度整合进 Hook 接口的体现。Authentication ProviderAuthentication Provider 集中管理 Refine 应用中的认证与授权流程包括登录、登出、重定向、错误处理等import { AuthProvider } from refinedev/core; export const authProvider: AuthProvider { login: async ({ email, password }) { const { status } handleLogin(email, password); if (status 200) { return { success: true, redirectTo: /dashboard }; } else { return { success: false, error: { name: Login Error, message: Invalid credentials }, }; } }, check: async (params) ({}), logout: async (params) ({}), onError: async (params) ({}), register: async (params) ({}), forgotPassword: async (params) ({}), updatePassword: async (params) ({}), getPermissions: async (params) ({}), getIdentity: async (params) ({}), };login方法返回{ success, redirectTo }或{ success: false, error }由 Refine 统一处理后续的重定向与错误提示。完整的认证方法集useLogin、useLogout、useRegister、useGetIdentity等可在 packages/core/src/hooks/auth/ 中查看。Components使用refinedev/core导出的Authenticated组件即可用认证保护你的路由与组件import { Authenticated } from refinedev/core; const MyPage () ( Authenticated // Only authenticated users can see this. MyComponent / /Authenticated );Hooks使用useGetIdentity获取当前用户信息import { useGetIdentity } from refinedev/core; export const DashboardPage () { const { data: { name }, } useGetIdentity(); return Welcome {name}!/; };UI Integrations各 UI 集成提供与 Auth Provider 开箱即用的预构建组件提供 Auth Provider 后其Layout 组件会自动在头部渲染当前用户信息并在合适的位置添加登出按钮同时可以使用这些集成的AuthPage组件快速搭建Login、Register、Forgot Password、Reset Password页面参见下文 Auth Pages 与 Authentication 指南。Access Control ProviderAccess Control Provider 基于用户权限决定其能访问或执行的操作。它利用 resource 定义来判断访问权限——例如根据products资源的定义决定用户能否编辑或删除该资源的记录import { AccessControlProvider, Refine } from refinedev/core; const myAccessControlProvider: AccessControlProvider { can: async ({ resource, action }) { if (resource users action block) { return { can: false }; } return { can: true }; }, }; export const App () { return ( Refine accessControlProvider{myAccessControlProvider}{/* ... */}/Refine ); };Components用CanAccess组件包裹应用中需要控制访问的部分import { CanAccess } from refinedev/core; export const MyPage () { return ( CanAccess resourceusers actionshow params{{ id: 1 }} My Page CanAccess resourceusers actionblock params{{ id: 1 }} fallback{You are not authorized.} // Only authorized users can see this button. BlockUserButton / /CanAccess / /CanAccess ); };Hooks使用useCanHook 在组件内控制访问实现见 packages/core/src/hooks/accessControl/useCan/index.tsimport { ErrorComponent, useCan } from refinedev/core; export const MyPage () { const { data: show } useCan({ resource: users, action: show, params: { id: 1 }, }); const { data: block } useCan({ resource: users, action: block, params: { id: 1 }, }); if (!show?.can) { return ErrorComponent /; } return ( My Page {block?.can BlockUserButton /} {!block?.can You are not authorized.} / ); };UI Integrations提供 Access Control Provider 后UI 集成会自动生效例如用户无权查看orders资源时侧边栏菜单会自动隐藏该项当前用户无权删除某商品时删除按钮会自动禁用或隐藏import { DeleteButton } from refinedev/antd; // or refinedev/mui, refinedev/chakra-ui, refinedev/mantine export const MyPage () { return ( My Page {/* Only authorized users can see this button. */} DeleteButton resourceusers recordItemId{1} / / ); };这一机制同样作用于CreateButton、EditButton、ShowButton、ListButton等所有按钮。Notification ProviderRefine 可以为 CRUD 操作与错误自动展示通知——例如创建、更新、删除products资源后或表单提交出错时。它内置了Ant Design、Material UI、Chakra UI、Mantine等主流 UI 库的通知 Provider。Hooks数据 Hooks、mutation Hooks、auth Hooks会自动为操作与错误展示通知并且支持按 Hook 自定义这些通知import { useDelete } from refinedev/core; export const MyPage () { const { mutate } useDelete(); return ( Button onClick{() { mutate({ resource: products, id: 1, successNotification: () ({ message: Product Deleted, description: Product has been deleted successfully., type: success, }), errorNotification: () ({ message: Product Delete Error, description: An error occurred while deleting the product., type: error, }), }); }} Delete Product /Button ); };对于未被覆盖的场景可以使用useNotificationHook 主动展示通知其open支持success | error | progress三种类型相关实现见 packages/core/src/hooks/notification/import { useNotification } from refinedev/core; export const MyPage () { const { open, close } useNotification(); return ( Button onClick{() { open?.({ key: my-notification, message: Test Notification, description: This is a test notification., type: success, // success | error | progress }); }} Show notification /Button Button onClick{() { close?.(my-notification); }} Close Notification /Button / ); };I18n ProviderI18n Provider 集中管理 Refine 应用中的本地化流程import { Refine, I18nProvider } from refinedev/core; const i18nProvider: I18nProvider { translate: (key: string, options?: any, defaultMessage?: string) string, changeLocale: (lang: string, options?: any) Promise, getLocale: () string, }; export const App () { return ( Refine i18nProvider{i18nProvider} {/* ...*/} {/* ... */} /Refine ) }Hooks使用useTranslate、useSetLocale、useGetLocale三个 Hooks 在组件中处理 i18n实现分别位于 packages/core/src/hooks/i18n/useTranslate.ts、packages/core/src/hooks/i18n/useSetLocale.ts、packages/core/src/hooks/i18n/useGetLocale.tsimport { useTranslate, useSetLocale, useGetLocale } from refinedev/core; export const MyPage () { const translate useTranslate(); const setLocale useSetLocale(); const getLocale useGetLocale(); return ( Current Locale: {getLocale()} Button onClick{() setLocale(en)}Set Locale to English/Button Button onClick{() setLocale(de)}Set Locale to German/Button Button{translate(Hello)/Button / ); };UI Integrations提供 I18n Provider 后UI 集成会自动翻译菜单项、按钮文本、表格列、页面标题等内容。Router ProviderRouter Provider 帮助 Refine 理解资源与路由之间的关系启用面包屑、CRUD 操作后的自动重定向、菜单项渲染、Hook 参数推断等导航能力。内置的路由集成包括React RouterNext.jsRemixExpo Router (React Native)ComponentsUI Integration组件可以从当前 URL 推断资源信息。例如我们在products资源的列表页使用List布局组件添加一个CreateButton来跳转到该资源的创建页——有了 router provider当前资源信息会从 URL 自动推断import { List, CreateButton } from refinedev/antd; // or refinedev/mui, refinedev/chakra-ui, refinedev/mantine export const ProductsListPage () { return ( // Instead of List resourceproducts List {/* Instead of CreateButton resourceproducts / */} CreateButton / // Redirects to /products/new /List ); };HooksRefine Hooks 可以从当前 URL 同步resource、id、action参数无需手动传入。例如useShow能自动推断resource和idimport { useShow } from refinedev/core; export const ShowPage () { const { result: product, query: { isLoading }, // useShow({ resource: products, id: 1 }); // We dont need to pass resource and id parameters manually. } useShow(); if (isLoading) { return Loading.../; } return {product?.name}/; };另一个例子是useTableHook它既能从当前路由推断resource、pagination、filters、sorters参数也会在这些参数变化时更新当前路由syncWithLocation行为。Audit Log ProviderAudit Log Provider 集中管理 Refine 应用中的审计日志获取可用于展示资源的变更历史import { AuditLogProvider, Refine } from refinedev/core; const auditLogProvider: AuditLogProvider { get: async (params) { const { resource, meta, action, author } params; const response await fetch( https://example.com/api/audit-logs/${resource}/${meta.id}, { method: GET, }, ); const data await response.json(); return data; }, }; export const App () { return Refine auditLogProvider{auditLogProvider}{/* ... */}/Refine; };Hooks使用useLogListHook 在组件中获取资源的审计日志它在底层调用AuditLogProvider的get方法import { useLogList } from refinedev/core; const productsAuditLogResults useLogList({ resource: products, });UI Integrations无头核心之上的界面层Refine 自身是无头的但为流行 UI 库提供了集成包Ant Design介绍文档Material UI介绍文档Chakra UI介绍文档Mantine介绍文档这些集成在底层使用refinedev/core充当 UI 库与 Refine 框架之间的桥梁。仓库中对应的集成实现分别位于 packages/antd、packages/mui、packages/chakra-ui、packages/mantine。FormsRefine 提供一组处理表单状态、校验、提交、自动保存等能力的 Hooks并能无缝衔接主流 UI 库的表单组件React Hook FormAnt Design FormMantine FormTablesRefine 与多个流行 UI 库的表格组件无缝集成简化分页、排序、过滤等特性的使用TanStack TableAnt Design TableMaterial UI DataGrid更完整的表格玩法可参考 Tables 指南。LayoutUI 集成提供Layout 组件负责渲染应用的侧边栏菜单、头部与内容区。它会基于resource 定义自动渲染侧边栏菜单并基于当前用户渲染头部信息。CRUD PagesList、Create、Edit、Show组件基于资源信息自动提供布局视图包括带标题的页头Header面包屑Breadcrumb翻译后的文本CRUD 按钮在此基础上Refine 还为这些布局附加了能力访问控制若当前用户无权创建商品创建按钮会自动禁用或隐藏翻译按钮、标题、列会翻译为用户的当前语言。Buttons例如 UI 集成导出的CreateButton用于将用户重定向到资源的创建页。按钮本身虽来自底层 UI 包但 Refine 为其附加了能力路由点击按钮后跳转到资源的创建页访问控制若当前用户无权操作按钮会自动禁用或隐藏翻译按钮文本会翻译为用户的当前语言。Auth PagesLogin、Register、Forgot Password、Reset Password等通用认证页面会自动与AuthProvider集成支持 Headless 与四种 UI 集成形态相关实战示例可参考 examples/auth-antd/、examples/auth-material-ui/ 等目录。UI Integration HooksUI 集成 Hooks 在底层使用refinedev/core的 Hooks使其更易用于 UI 特定的组件中。例如refinedev/antd包的useTableHook底层使用refinedev/core的useTable但返回与 Ant DesignTable组件兼容的 props——无需手动映射属性。Meta 概念跨层传递附加信息meta是一个特殊属性用于向providers与UI Integrations提供额外信息。它有3 种填充来源最终会被合并为单一的meta属性并传递给 providers 与 UI 集成来自 resource在Refine resources{...}定义中声明import { Refine } from refinedev/core; export const App () { return ( Refine resources{[ { name: products, list: /my-products, meta: { fromResource: Hello from resource.meta, }, }, ]} {/* ... */} /Refine ); };来自 Hook在调用 Hook 时传入import { useShow } from refinedev/core; export const ShowPage () { const { query: { data, isLoading }, /* or use useOne */ } useShow({ resource: posts, id: 1, meta: { fromHook: Hello from hook.meta, }, }); };来自 URL通过 URL 查询参数传递https://example.com/products?fromURLHello%20from%20URL合并后三个来源的 meta 字段会在 Provider 中同时可见import { AccessControlProvider, DataProvider } from refinedev/core; export const myDataProvider { getOne: async ({ meta }) { console.log(meta.fromResource); // Hello from resource.meta console.log(meta.fromHook); // Hello from hook.meta console.log(meta.fromURL); // Hello from URL }, }; export const myAccessControlProvider { can: async ({ meta }) { console.log(meta.fromResource); // Hello from resource.meta console.log(meta.fromHook); // Hello from hook.meta console.log(meta.fromURL); // Hello from URL }, };典型应用场景全局过滤器向 data provider 传递一个过滤器多租户将当前租户 ID 提供给 providers高级访问控制按资源进行访问控制配置UI 定制按资源管理侧边栏标签与图标。需要注意的是meta 会影响查询键的生成——下文状态管理部分会说明Refine 将 meta 属性视为 key 的一部分会依据 meta 内容区分查询相关 Context 见 packages/core/src/contexts/metaContext/。状态管理基于 React Query 的结构化键体系Refine 使用React Query处理数据获取与缓存提升应用性能与用户体验高效地实现服务器与 UI 之间的数据同步、后台更新、缓存管理与数据失效。数据获取、缓存管理与去重Refine 使用结构化键structured keys来标识并缓存查询与变更的服务器响应可复用时复用缓存数据以优化性能。可组合的结构化键还支持查询自动去重同一查询被多处调用时只发出一次请求并在所有订阅者间共享结果。默认配置下Refine 的查询缓存时间为 5 分钟、过期stale时间为 0 秒查询在 5 分钟内被再次使用会先用缓存数据填充同时在后台重新获取超过 5 分钟未复用则立即重新获取。失效与重新获取基于结构化键的状态管理也帮助在 mutation 发生时自动失效相关查询。例如用户创建一条新记录后Refine 会自动使相关查询失效保证用户看到的数据始终与后端一致。默认行为是所有相关查询都会失效但只有当前正在使用的查询才会重新获取——如果用户不在某资源的列表页该列表查询不会被重新获取只被标记为失效待用户导航到列表页时再拉取最新数据。失效与重新获取行为可以通过 mutation 的invalidates属性自定义或在Refine /组件上全局配置参见 Forms 指南的 Invalidation 章节。乐观更新与回滚为用户提供即时反馈至关重要Refine 通过乐观更新optimistic updates实现mutation 发生时自动用新数据更新相关查询用户立即看到变化若 mutation 失败则自动回滚更改并重新获取相关查询。Refine 提供 3 种mutation modepessimistic悲观、optimistic乐观、undoable可撤销。乐观更新会在optimistic与undoable模式下执行undoable模式还会通过通知让用户在指定时间内撤销更改。默认的乐观更新行为Updatemutation对目标资源的列表list、many 与详情detail查询执行乐观更新Createmutation对目标资源的列表与 many 查询执行乐观更新Deletemutation对目标资源的列表与 many 查询执行乐观更新。可以通过 Hooks 的optimisticUpdateMap与mutationMode属性定制或通过Refine /组件全局配置更多细节见 Forms 指南的 Optimistic Updates 章节。键结构Key Structure键用于标识并缓存查询与变更的服务器响应。Refine 采用可重组的结构化键格式使用相同参数重新组合即可得到相同键从而让开发者完全掌控应用的缓存与失效行为。所有查询缓存与变更都可以通过这些键进行跟踪和管理。refinedev/core暴露了keys方法用于生成查询与变更的键如果需要对缓存做高级操作可以用它生成键并获取对应的查询或变更缓存。键的结构化层级从通用到具体最外层操作类型信息可为auth、data、audit或access若是data类型下一层包含其使用的 data provider 信息再下一层包含其操作的 resource 信息资源信息之后下一层是操作类型可为list、infinite、many、one最后一层是操作参数可为filters、sorters、pagination、id等以及meta属性的内容注意Refine 将meta属性视为键的一部分并依据meta属性区分查询。例如带filters的products资源列表查询的键生成如下import { useList, keys } from refinedev/core; const Component () { const response useList({ resource: products, filters: [ { field: title, operator: contains, value: test, }, ], }); // This key will be generated by useList and used to identify the query and cache the response. const generatedKey keys() .data(default) // Name of the data provider .resource(products) // Identifier of the resource .action(list) // Type of the operation .params({ filters: [{ field: title, operator: contains, value: test }], }) // Parameters of the operation .get(); console.log(generatedKey); // ^ [data, default, products, list, { filters: [{ field: title, operator: contains, value: test }] }] };这套键结构让缓存管理与失效行为高度可预测、可追踪是 Refine 状态管理的基石。开发者体验CLI、Devtools 与 InferencerCLIRefine CLI 允许你与 Refine 项目交互并执行特定任务例如创建新资源、管理版本更新、swizzle 组件、运行项目build、start、dev。CLI 包实现位于 packages/cli更多用法见 Packages 文档。DevtoolsRefine Devtools旨在帮助你调试和开发 Refine 应用功能集包括监控查询与变更、测试 inferencer 生成的代码、从 UI 中添加和更新 Refine 包等。相关实现见 packages/devtools 及其配套的 packages/devtools-server、packages/devtools-ui 等包。Inferencerrefinedev/inferencer是一个根据 API 响应自动生成基础样板代码的包作为节省时间的起点。需要注意的是它并非对所有场景都可靠且不适用于生产环境。例如以下代码即可脚手架出完整的 CRUD 页面import { AntdInferencer } from refinedev/inferencer/antd; // or refinedev/inferencer/mui, refinedev/inferencer/chakra, refinedev/inferencer/mantine, refinedev/inferencer/headless export const ProductList () { // Scaffolds List page. return AntdInferencer /; }; export const ProductShow () { // Scaffolds Show page. return AntdInferencer /; }; export const ProductEdit () { // Scaffolds Edit page with form. return AntdInferencer /; }; export const ProductCreate () { // Scaffolds Create page with form. return AntdInferencer /; };一个由 inferencer 生成的List Page示例基于 Ant Designimport { List, ShowButton, useTable } from refinedev/antd; import { BaseRecord } from refinedev/core; import { Space, Table } from antd; import React from react; export const ProductList () { const { tableProps } useTable({ syncWithLocation: true, }); return ( List Table {...tableProps} rowKeyid Table.Column dataIndexid titleId / Table.Column dataIndexname titleName / Table.Column dataIndexprice titlePrice / Table.Column titleActions dataIndexactions render{(_, record: BaseRecord) ( Space ShowButton hideText sizesmall recordItemId{record.id} / /Space )} / /Table /List ); };可以看到生成代码直接使用了useTable含syncWithLocation路由同步、List布局与ShowButton可作为真实业务代码的起点再按需调整。Inferencer 各 UI 形态的实现见 packages/inferencer实战示例可参考 examples/inferencer-antd/、examples/inferencer-headless/ 等目录。小结Refine 的通用概念可以归纳为一条清晰的主线Headless 架构保证业务逻辑与 UI 解耦Resource将数据实体、路由与操作结构化Provider以可插拔方式注入数据、认证、权限、通知、i18n、实时、路由与审计能力Hook为所有这些能力提供跨库统一的接口meta打通资源、Hook 与 URL 之间的信息传递而基于 React Query 的结构化键状态管理则让缓存、去重、失效与乐观更新变得可预测、可掌控。无论是从零开始还是借助 CLI、Devtools 与 Inferencer 加速开发这六大概念都是深入理解与高效使用 Refine 的钥匙。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表