如何把 Refine 集成进现有 React 项目并挂载到 /refine 路由?

原创2026-09-09 23:18:44299 阅读
文章标签:前端企业应用

如何把 Refine 集成进现有 React 项目并挂载到 /refine 路由?

如果你已经有一个运行中的 React 应用(Vite、Next.js 等),想把 Refine 的管理面板能力挂到应用的 /refine 路由下,而不是让 Refine 接管整个应用,Refine 官方提供了完整的接入方式:安装 @refinedev/core 及相关包,用一个 Refine 组件包裹 /refine 路由下的页面,其余现有路由保持原样。本文章节对应文档 Usage with Existing Projects。

集成原理与准备条件

Refine 组件为所有子组件提供必要的 context,Refine 的 hooks 和组件依赖这个 context 才能工作。Refine 组件唯一的必填 prop 是 dataProvider,关于 data provider 的介绍见 Data Provider 文档。

因此集成思路是:

  • 如果只在个别组件里用 Refine hooks(比如 useShow),可以直接在 App.tsx 里用 Refine 组件包裹这些组件,无需路由配置;
  • 如果想把 Refine 页面挂载到 /refine 路由,则需要为 /refine 路由单独准备一个 Refine 上下文组件(含 routerProvider),现有路由不动。

本文按第二种目标展开。准备条件只有一个:项目中已有 React 环境和可用的包管理器。

安装依赖

在现有项目根目录安装以下包(以 npm 为例,pnpm add / yarn add 同理):

npm i @refinedev/core @refinedev/react-router @refinedev/simple-rest
  • @refinedev/core:核心包,Refine 运行的唯一必需依赖;
  • @refinedev/react-router:路由适配包(routerProvider),Next.js 项目改用 @refinedev/nextjs-router;
  • @refinedev/simple-rest:文档示例中用作 data provider 的 REST 适配器,你可以换成项目自己的或任意的 data provider。

Vite + React Router 项目:挂载 /refine 路由

文档给出的分步说明如下:先在 refine/refine-context.tsx 创建 RefineContext 组件,用于包裹应用的 /refine 路由;然后在 App.tsx 中新增一个 path="/refine" 的 Route,并用 RefineContext 包裹;最后创建 refine/pages/products/list.tsx,由于它的布局被 Refine 组件包裹,即可使用 Refine 的各项功能。

第 1 步:创建 refine/refine-context.tsx

import { Refine } from "@refinedev/core";
import routerProvider from "@refinedev/react-router";
import dataProvider from "@refinedev/simple-rest";

export function RefineContext({ children }) {
  return (
    <Refine
      routerProvider={routerProvider}
      dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
      resources={[
        {
          name: "products",
          list: "/refine/products",
        },
      ]}
      options={{ syncWithLocation: true }}
    >
      {children}
    </Refine>
  );
}

dataProvider 中的 https://api.fake-rest.refine.dev 是文档示例使用的演示 API 地址,实际使用时替换为你自己的后端地址。resources 中声明了 products 资源,其 list 页面位于 /refine/products。

第 2 步:在 App.tsx 中注册 /refine 路由

import { BrowserRouter, Route, Routes, Outlet } from "react-router";

import { ErrorComponent } from "@refinedev/core";
import { NavigateToResource } from "@refinedev/react-router";

import { Home } from "./pages/home.tsx";
import { About } from "./pages/about.tsx";
import { ProductList } from "./refine/pages/products/list.tsx";
import { RefineContext } from "./refine/refine-context.tsx";

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Your existing routes */}
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        {/* Refine routes */}
        <Route
          path="/refine"
          element={
            <RefineContext>
              <Outlet />
            </RefineContext>
          }
        >
          <Route index element={<NavigateToResource />} />
          <Route path="products" element={<ProductList />} />
          <Route path="*" element={<ErrorComponent />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

注意路由结构:/refine 路由的元素是 <RefineContext> 包裹的 <Outlet />,Refine 的页面作为子路由挂在它下面。index 路由使用 NavigateToResource,访问 /refine 时会自动导向 resources 中声明的列表页。Home / About 代表你现有的页面,它们不在 Refine 上下文内,不受影响。

第 3 步:创建 refine/pages/products/list.tsx

import { useList } from "@refinedev/core";

export const ProductList = () => {
  const { result } = useList();
  const products = result?.data;

  return (
    <div>
      <h1>Refine Products Page</h1>
      <ul>
        {products?.map((record) => (
          <li key={record.id}>{record.name}</li>
        ))}
      </ul>
    </div>
  );
};

结果验证

在应用首页放置一个指向 /refine 的链接(文档示例的 Home 页即如此)。验证路径如下:

  1. 访问 /refine,应被 NavigateToResource 导向 /refine/products;
  2. 该页面使用 useList 拉取数据,应渲染出产品列表——文档演示环境返回的是 https://api.fake-rest.refine.dev 的产品数据,换成自己的后端后应返回对应数据。

如果你暂时只想验证 hooks 可用而不挂路由,可以用文档的 headless 示例:在 App.tsx 中用 <Refine dataProvider={dataProvider(API_URL)}> 直接包裹组件,组件内使用 useShow({ resource: "products", id: 1 }),能拿到查询结果即说明 context 生效。

可选:为 /refine 添加 UI 布局

上一步的 /refine 页面是纯 hooks 渲染。如果希望有现成的管理界面,文档的后续示例是引入 @refinedev/antd 的 Ant Design 布局。额外安装:

npm i @refinedev/antd

对应的 refine/refine-context.tsx 改为:

import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";
import routerProvider from "@refinedev/react-router";
import { RefineThemes, ThemedLayout } from "@refinedev/antd";

import { App as AntdApp, ConfigProvider } from "antd";

import "@refinedev/antd/dist/reset.css";

export function RefineContext({ children }) {
  return (
    <ConfigProvider theme={RefineThemes.Blue}>
      <AntdApp>
        <Refine
          routerProvider={routerProvider}
          dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
          resources={[
            {
              name: "products",
              list: "/refine/products",
            },
          ]}
          options={{ syncWithLocation: true }}
        >
          <ThemedLayout>{children}</ThemedLayout>
        </Refine>
      </AntdApp>
    </ConfigProvider>
  );
}

同时做两处更新:App.tsx 中的 ErrorComponent 改从 @refinedev/antd 导入;列表页改用 @refinedev/antd 的 List 组件与 useTable hook:

import { List, useTable } from "@refinedev/antd";
import { Table } from "antd";

export const ProductList = () => {
  const { tableProps } = useTable();

  return (
    <List>
      <Table {...tableProps} rowKey="id">
        <Table.Column dataIndex="id" title="Id" />
        <Table.Column dataIndex="name" title="Name" />
        <Table.Column dataIndex="price" title="Price" />
      </Table>
    </List>
  );
};

其他 UI 库(Material UI、Chakra UI、Mantine 等)的接入方式见 UI Libraries 指南。

Next.js 项目的接入差异

Next.js 项目用 @refinedev/nextjs-router 替代 @refinedev/react-router:

npm i @refinedev/core @refinedev/nextjs-router @refinedev/simple-rest
  • App Router:文档的做法是创建 app/refine/layout.tsx,这个 layout 会被 /refine 文件夹下所有页面复用,页面文件(如 app/refine/products/page.tsx)自然被 Refine 包裹。layout 需声明 "use client":
"use client";
import { Refine } from "@refinedev/core";
import routerProvider from "@refinedev/nextjs-router";
import dataProvider from "@refinedev/simple-rest";

export default function RefineLayout({ children }: { children: React.ReactNode }) {
  return (
    <Refine
      routerProvider={routerProvider}
      dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
      resources={[
        {
          name: "products",
          list: "/refine/products",
        },
      ]}
      options={{ syncWithLocation: true }}
    >
      {children}
    </Refine>
  );
}

之后在 app/refine/products/page.tsx 中即可直接使用 useList 等 hooks(该页面同样需要 "use client")。

  • Pages Router:文档的做法是创建 src/components/refine-layout.tsx(Refine 上下文组件,routerProvider 从 @refinedev/nextjs-router/pages 导入),在 pages/_app.tsx 中根据页面是否定义了 getLayout 决定性地包裹页面,再给 pages/refine/products.tsx 挂上 Page.getLayout,使其被 Refine 上下文包裹。需要说明的是,当前仓库中该示例文件(nextjs/pages/router.tsx)存在明显笔误:RefineLayout 的返回 JSX 中残留了未导入的 <AntdApp>、<ConfigProvider> 标签。按文档说明的逻辑,该组件的正确形态应与上面 App Router 的 layout 一致,即直接返回 <Refine ...>{children}</Refine>。

复用现有项目的认证

如果现有应用已经有登录态,文档的结论是:为了启用 Refine 的 Authentication 功能,只需要实现 AuthProvider 的 check 方法即可,登录、登出逻辑继续走你现有的方案。提供 check 之后,即可使用 Authenticated 组件和 useIsAuthenticated hook,Refine 会把未认证用户重定向到你指定的登录页:

import { AuthProvider } from "@refinedev/core";

export const authProvider: AuthProvider = {
  check: async () => {
    const isAuthenticated = myCheckLogic();

    if (isAuthenticated) {
      return { authenticated: true };
    }

    return {
      authenticated: false,
      redirectTo: "/my-login-page",
      error: {
        name: "Authentication Failed.",
        message: "User not found.",
      },
    };
  },
  login: async () => {
    throw new Error("Method not implemented.");
  },
  logout: async () => {
    throw new Error("Method not implemented.");
  },
  onError: async () => {
    throw new Error("Method not implemented.");
  },
};

其中 myCheckLogic() 与 "/my-login-page" 是文档中的占位写法,替换为你自己的登录态判断和登录页路径。getIdentity 可选实现,用于启用 useGetIdentity hook(也用于 UI 布局头部显示当前用户);logout 可选实现,用于启用 useLogout hook(也用于侧边栏的登出按钮)。

参考文档

登录后查看全文
refine