1. Refine的基本功能与架构理念
Refine是一个基于React的元框架,核心目标是简化数据密集型应用的开发。它采用 以资源为中心 的架构,开发者通过声明资源(Resource)、提供数据适配器(DataProvider)、注册路由和UI组件,就可以快速搭建符合CRUD模式的页面。
它的设计优势在于:
资源驱动:一切以 resource 为核心组织;
Hook 优先:数据获取、表单处理、权限等逻辑,都有对应的 Hook;
可替换性强:UI、数据源、权限、路由都可替换,保持开放性。
Provider抽象 —— 统一封装数据源、鉴权、通知、国际化等跨应用能力。
这种模式非常适合标准化程度较高的后台管理系统,但在BOM管理系统这种 复杂企业应用 中,会遇到一些新问题。
1.1 Resources概念
Refine中的Resource是指应用程序中的数据实体,如用户、产品、订单等。它是Refine的核心思想。每个Resource都包含了一组标准的CRUD操作和相关的配置信息。通过定义Resource,开发者可以快速生成完整的数据管理界面。
import { Refine } from "@pankod/refine-core";
const App = () => {
return (
<Refine
dataProvider={dataProvider}
routerProvider={routerProvider}
resources={[
{
name: "products",
list: "/products",
create: "/products/new",
edit: "/products/:id/edit",
show: "/products/:id",
},
{
name: "categories",
list: "/categories",
create: "/categories/new",
edit: "/categories/:id/edit",
show: "/categories/:id",
meta: {
canDelete: true,
},
},
]}
/>
);
};
这种以资源为中心的设计理念,使得开发者可以专注于业务逻辑而非基础架构代码。每个Resource都包含了列表、创建、编辑、查看等基本操作,这些操作通过约定的接口与数据提供者(DataProvider)进行交互。
1.2 与Router的集成
Refine与路由系统的深度集成是其另一个重要特性。通过将Resource与路由关联,Refine可以自动生成符合RESTful规范的路由结构,简化了导航和页面管理。
import { RouterProvider } from "@pankod/refine-react-router-v6";
const App = () => {
return (
<Refine
routerProvider={RouterProvider}
// ...其他配置
/>
);
};
这种集成使得开发者可以通过简单的配置实现复杂的路由结构,同时保持了代码的可维护性和可扩展性。
1.3 Hook系统
Refine提供了一系列强大的Hook,用于简化数据获取、表单处理、权限控制等常见任务。这些Hook基于React的Hooks API,提供了声明式的编程模型。
import { useTable, useForm, useShow } from "@pankod/refine-core";
const ProductList = () => {
const { tableProps, sorter } = useTable({
resource: "products",
});
return (
<Table {...tableProps}>
{/* 表格内容 */}
</Table>
);
};
const ProductEdit = () => {
const { formProps, saveButtonProps } = useForm({
resource: "products",
action: "edit",
});
return (
<Form {...formProps}>
{/* 表单内容 */}
</Form>
);
};
这些Hook封装了常见的CRUD操作、表单验证、数据获取等逻辑,使开发者可以更专注于业务实现。
2. 实际开发中遇到的难题
尽管Refine提供了优雅的架构设计和丰富的功能,但在BOM管理系统这样的复杂企业应用中,我们也发现了一些实际应用中的挑战。 它面临的问题通常包括:
-
多数据源页面:一个页面往往需要同时加载产品节点、特性列表、配置层级等数据。
-
复杂表单逻辑:字段间强依赖,可能需要动态显示、批量联动。
-
细粒度权限:不仅是页面级 CRUD,还涉及字段级和数据范围级。
-
复杂查询:过滤条件多样,涉及到跨资源的组合查询。
这些挑战导致 开发人员常常在业务代码和基础组件之间切换,而 Refine 的默认模式更偏向单一资源的 CRUD,不完全匹配。
2.1 复杂的数据源管理
在BOM管理系统中,一个页面的数据往往来自多个不同的API接口。 例如,在工程配置表中,我们需要同时获取产品节点、配置层级、特性列表、规则定义等多个数据源。Refine的Resource模型假设一个页面主要围绕一个核心资源进行操作,这在BOM系统中往往不够灵活。
// 当前项目中的工程配置表组件
const EngineeringConfig = (props) => {
const {
featureTableId,
config,
selectAbleVariableVehicles, // 已选的配置层级
productNodeCodes,
params,
preProductNode,
} = props;
// 需要从多个不同的API获取数据
const { onRefresh, handleSearch, onSearch, loading } = useConfigTableSearch(
namespace,
onFetch,
setModelState,
{
featureType: 'E',
},
setSyncData,
);
// 其他复杂的业务逻辑...
};
2.2 复杂的表单处理
BOM管理系统中的表单往往非常复杂,字段之间可能存在复杂的依赖关系,或者需要根据不同的条件动态显示/隐藏某些字段。例如,在变更内容表单中,根据用户选择的不同操作类型,表单的字段和验证规则会发生变化。
// 当前项目中的变更内容表单
const onCellEditingChange = (_rowData, fieldName, newValue, oldValue) => {
const rowData = _.cloneDeep(_rowData);
switch (fieldName) {
case 'featureVersionDto.groupNum': {
// 当临时编码族发生变化时,清空父特征相关字段
if (newValue) {
[
'featureVersionDto.parentFeatureCode',
'featureVersionDto.parentId',
'featureVersionDto.parentFeature',
].forEach((field) => {
_.set(rowData, field, void 0);
});
}
// 更新同临时编码族其他数据
if (oldValue && newValue !== oldValue) {
const children = dataSource.filter(
(item) => item.featureVersionDto.groupNum === oldValue && item.id !== rowData.id,
);
const updatedChildren = children.map((item) => ({
...item,
featureVersionDto: {
...item.featureVersionDto,
groupNum: newValue,
},
}));
gridManagerRef.current?.modify(updatedChildren);
}
break;
}
// 其他字段的处理...
}
return rowData;
};
2.3 复杂的权限控制
BOM管理系统中的权限控制通常非常复杂,不仅包括基本的CRUD权限,还包括字段级别的权限控制、数据范围控制等。例如,在工程配置表中,不同角色的用户可能只能看到或编辑某些特定的字段。
// 当前项目中的权限控制
const { hasVersion, configTabsAuth } = useAuth(_configTabsAuth, searchParams);
// 功能点权限
const { AuthData, setDataId } = useCheckAuth(
'工程配置表',
_.get(searchParams, 'productNode.nodeCode') ? `${_.get(searchParams, 'productNode.nodeCode')}:E` : undefined,
);
const { isAuthInitImport = true, isAuthExport, isAuthEdit = false } = AuthData;
2.4 复杂的搜索和过滤
BOM管理系统中的搜索和过滤功能通常非常复杂,可能需要同时查询多个数据源,或者根据复杂的业务规则进行数据过滤。例如,在工程配置表中,用户可能需要根据产品节点、配置层级、特性版本等多个条件进行组合查询。
// 当前项目中的搜索逻辑
const useConfigTableSearch = (
namespace,
onFetch,
setModelState,
defaultParams,
setSyncData,
) => {
const searchRep = usePageSearch(async (filterInfo, pageInfo) => {
// 复杂的查询逻辑
const { content, totalCount } = await findContentListApi({
filterInfo: {
...filterInfo,
// 可能需要添加其他查询条件
},
pageInfo,
});
setDataSource(content);
return totalCount;
}, defaultParams);
return searchRep;
};
3. 针对BOM管理系统的开发范例
Refine 的优势是“开放式的接口”,并不限制我们扩展。针对 BOM 系统的复杂性,可以从 开发体验 出发,做以下改造。
3.1 多数据源的组织方式
Refine 默认 useTable 针对单一资源,但 BOM 页面常常依赖多个资源。 我们可以在 自定义 Hook 中封装多数据源逻辑,让页面开发者只需要关注业务状态。
// 一个自定义 hook,把多数据源整合为业务态
const useEngineeringConfig = () => {
const products = useList({ resource: "products" });
const nodes = useList({ resource: "product-nodes" });
const features = useList({ resource: "features" });
// 组合后的业务数据
const engineeringConfig = useMemo(() => {
return mergeProductsAndNodes(products.data, nodes.data, features.data);
}, [products.data, nodes.data, features.data]);
return { engineeringConfig, loading: products.isLoading || nodes.isLoading };
};
// 页面代码
const EngineeringConfigPage = () => {
const { engineeringConfig, loading } = useEngineeringConfig();
if (loading) return <Spin />;
return <EngineeringConfig data={engineeringConfig} />;
};
这样,开发者在页面层只需要关心 业务态数据(engineeringConfig),而不是去管理多个 useList。
3.2 表单逻辑的扩展
Refine 提供了 useForm,但 BOM 的表单往往需要动态依赖。 最佳实践是基于 useForm 封装 业务级表单 Hook,让业务逻辑与 UI 解耦。
const useChangeForm = () => {
const { formProps, saveButtonProps } = useForm({ resource: "changes" });
// 增强:动态联动逻辑
const onFieldChange = (field: string, value: any) => {
if (field === "groupNum") {
formProps.form?.setFieldsValue({
parentFeatureCode: undefined,
parentId: undefined,
});
}
};
const schema = {/* 动态表单字段 */}
return { schema, formProps, saveButtonProps, onFieldChange };
};
// 页面代码
const ChangeForm = () => {
const { formProps, saveButtonProps, onFieldChange } = useChangeForm();
return (
<Form {...formProps} onValuesChange={onFieldChange}>
<Button {...saveButtonProps}>保存</Button>
</Form>
);
};
这种模式下,复杂逻辑被封装在 Hook 内,表单开发更“爽”:
页面代码专注渲染;
逻辑集中在 Hook,方便复用和测试。
3.3 权限控制的细粒度扩展
Refine 内置了 accessControlProvider,适合做资源级别权限。 在 BOM 系统中,可以在其上层再包一层,支持 字段级和条件级权限。
// 封装字段权限 Hook
const useFieldPermission = (resource: string, field: string) => {
const { can } = useCan({ resource, action: "edit", field });
return can;
};
// 使用
const EngineeringForm = () => {
const canEditFeature = useFieldPermission("engineering-config", "featureCode");
const schema = {
featureCode: {
title: '特征编码',
disabled: !canEditFeature
}
}
return <Form schema={schema} />;
};
这样,开发者在写业务表单时,只需要关心 useFieldPermission 的返回值,而不用关心复杂的权限逻辑实现。
3.4 复杂搜索的抽象
Refine 的 useTable 默认支持简单的 filter,但 BOM 查询往往跨多个条件。 最佳实践是:对搜索逻辑二次封装 Hook,返回统一的搜索 API。
const useBOMSearch = () => {
const [filters, setFilters] = useState({});
const { tableProps, search } = useTable({
resource: "engineering-config",
initialSorter: [{ field: "createdAt", order: "desc" }],
});
// 封装复杂的组合查询
const onSearch = (params: any) => {
const merged = transformSearchParams(params);
setFilters(merged);
search(merged);
};
return { tableProps, onSearch };
};
开发者使用时:
const EngineeringConfig = () => {
const { tableProps, onSearch } = useBOMSearch();
return (
<>
<SearchBar onSubmit={onSearch} />
<Table {...tableProps} />
</>
);
};
页面层就很清晰:只需要调用 onSearch,无需理解复杂的参数拼装逻辑。
4. 总结
Refine 的强项是提供了 资源化的开发模式,让 CRUD 页面快速落地。 在 BOM 管理系统这种复杂场景下,开发者需要:
自定义 Hook,把多数据源、复杂表单、权限、搜索等逻辑封装起来;
页面层只关心“业务状态”和“UI渲染”;
重复逻辑统一下沉到 Hook 或 Provider 中。
这样既保留了 Refine 的优雅,又让开发体验更“爽”——写页面的时候更像是在拼积木,而不是在处理底层复杂度。