
Refine v5 中 Ant DesignShow組件完全指南從布局到源碼級解析【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refineShow是 Refine v5 的 Ant Design 集成包refinedev/antd中用于展示單條記錄詳情頁的布局組件。它本身不包含任何業務邏輯卻能在不寫一行額外代碼的情況下提供頁面標題、返回按鈕、刷新按鈕、面包屑以及可選的編輯/刪除入口。本文以該組件的官方文檔為主體結合倉庫內refinedev/antd包的源碼實現與測試用例系統講解Show的全部屬性、用法與底層工作原理幫助你快速構建生產可用的詳情展示頁。Show是什么一個無邏輯的頁面布局按官方文檔的定義Showprovides us a layout for displaying the page. It does not contain any logic but adds extra functionalities like a refresh button or giving title to the page.也就是說Show只負責搭架子它渲染出一個標準的詳情頁外殼頁頭 卡片內容區并順帶提供刷新按鈕、頁面標題等增強功能。真正取數據的工作由 Refine 核心的useShowHook 完成兩者配合使用。從源碼實現看packages/antd/src/components/crud/show/index.tsxShow接收title、canEdit、canDelete、deleteButtonProps、isLoading、resource、recordItemId、dataProviderName、breadcrumb、contentProps、headerProps、wrapperProps、headerButtons、footerButtons、footerButtonProps、headerButtonProps、goBack等屬性內部通過useResourceParams、useToPath、useBack、useGo、useTranslate、useRefineContext等 Refine 核心 Hook 完成資源解析、路徑跳轉與國際化最終渲染結構為div wrapperProps PageHeader backIcon{goBack} onBack{...} title{title 或 Show xxx} extra{headerButtons} breadcrumb{Breadcrumb} {...headerProps} Spin spinning{isLoading} Card variantborderless actions{footerButtons} {...contentProps} {children} /Card /Spin /PageHeader /div可以看到外層是普通div頁頭用的是 Ant Design Pro 的PageHeader內容區是 Ant Design 的Card加載態由Spin包裹。基礎用法與useShow組合展示帖子詳情下面是一個完整的帖子詳情頁示例。它先用useShowIPost()從當前路由解析出的resource與id拉取數據再用useOneICategory()關聯查詢分類名稱最后把數據交給Show渲染import { Show, MarkdownField } from refinedev/antd; import { Typography } from antd; import { useShow, useOne } from refinedev/core; const { Title, Text } Typography; interface ICategory { id: number; title: string; } interface IPost { id: number; title: string; content: string; status: published | draft | rejected; category: { id: number }; } const PostShow: React.FC () { const { result: post, query: { isLoading }, } useShowIPost(); const { data: categoryData, isLoading: categoryIsLoading } useOneICategory({ resource: categories, id: post?.category.id || , queryOptions: { enabled: !!post, }, }); return ( Show isLoading{isLoading} Title level{5}Id/Title Text{record?.id}/Text Title level{5}Title/Title Text{record?.title}/Text Title level{5}Category/Title Text{categoryIsLoading ? Loading... : categoryData?.data.title}/Text Title level{5}Content/Title MarkdownField value{record?.content} / /Show ); };配套的路由配置中需要把show路徑注冊到資源上例如Refine resources{[ { name: posts, list: /posts, show: /posts/show/:id, }, ]} Routes Route path/posts element{...} Route index element{ RefineAntd.ShowButton recordItemId123Show Item 123/RefineAntd.ShowButton } / Route pathshow/:id element{PostShow /} / /Route /Routes /Refine其中MarkdownField用于把 Markdown 內容渲染為富文本實現在 packages/antd/src/components/fields/markdown/index.tsxShowButton則是用于跳轉到詳情頁的按鈕組件見 packages/antd/src/components/buttons/show/index.tsx。補充Show組件是可被 swizzle 定制的。通過Refine CLI的 swizzle 命令你可以把組件源碼復制到自己的項目中直接修改官方文檔對此有專門說明Refine CLI。屬性詳解Propertiestitle自定義頁頭標題title允許你在Show內部添加標題。如果不傳該屬性組件會默認使用 Show 前綴加上資源的單數形式名稱。例如對posts資源默認標題就是 Show post。import { Show } from refinedev/antd; const PostShow: React.FC () { return ( Show titleCustom Title pRest of your page here/p /Show ); };從源碼index.tsx可以看到默認標題的完整生成邏輯優先取title否則用useTranslate查找${identifier}.titles.show的國際化詞條再回退到Show ${getUserFriendlyName(resource?.meta?.label ?? identifier, singular)}即把資源名轉成用戶友好形式如posts→post。resource指定自定義資源Show默認從路由中讀取resource信息。如果你需要在非標準路由上使用它可以顯式傳入resource屬性import { Show } from refinedev/antd; const CustomPage: React.FC () { return ( Show resourceposts pRest of your page here/p /Show ); };注意該屬性最終會傳入useResourceParams({ resource: resourceFromProps })因此它也支持傳資源對象而不僅是字符串。當你存在多個同名資源時可以改用identifier傳值——identifier僅作為資源匹配的主鍵而數據提供者data provider的方法調用仍會使用Refine/組件中定義的資源name。更多細節可參考Refine/組件的identifier文檔。canDelete與canEdit控制刪除/編輯按鈕這兩個布爾屬性用于在Show內部添加刪除與編輯按鈕點擊刪除按鈕會執行 data provider 提供的useDelete即deleteOne方法點擊編輯按鈕會把用戶重定向到該記錄的編輯頁。典型用法是結合usePermissions做權限控制——只有管理員才顯示操作按鈕import { Show } from refinedev/antd; import { usePermissions } from refinedev/core; const PostShow: React.FC () { const { data: permissionsData } usePermissions(); return ( Show canDelete{permissionsData?.includes(admin)} canEdit{permissionsData?.includes(admin)} pRest of your page here/p /Show ); };源碼中的判定邏輯index.tsx值得注意const hasList resource?.list !recordItemId; const isDeleteButtonVisible canDelete ?? (resource?.meta?.canDelete || deleteButtonPropsFromProps); const isEditButtonVisible canEdit ?? !!resource?.edit;即canDelete優先采用顯式傳入的值否則回退到資源meta.canDelete甚至只要傳了deleteButtonProps也會讓刪除按鈕顯示canEdit優先采用顯式值否則回退到資源是否配置了edit路徑。這些分支在測試文件 packages/antd/src/components/crud/show/index.spec.tsx 中都有對應的用例覆蓋例如資源canEdit: false但組件傳canEdit{true}時按鈕仍然渲染。deleteButtonProps定制刪除按鈕如果資源具備刪除能力并且你想調整刪除按鈕的外觀或行為可以傳入deleteButtonPropsimport { Show } from refinedev/antd; import { usePermissions } from refinedev/core; const PostShow: React.FC () { const { data: permissionsData } usePermissions(); return ( Show canDelete{permissionsData?.includes(admin)} deleteButtonProps{{ size: small }} canEdit{permissionsData?.includes(admin)} pRest of your page here/p /Show ); };源碼在組裝刪除按鈕屬性時index.tsx會自動補充recordItemId、onSuccess跳回列表頁等默認行為然后與你傳入的deleteButtonProps做淺合并const deleteButtonProps: DeleteButtonProps | undefined isDeleteButtonVisible ? { ...(isLoading ? { disabled: true } : {}), resource: identifier, recordItemId: id, onSuccess: () { go({ to: goListPath }); }, dataProviderName, ...deleteButtonPropsFromProps, } : undefined;類型上它是DeleteButtonProps可參考 packages/antd/src/components/buttons/types.ts即 Ant DesignButton的屬性與 Refine 刪除按鈕能力的并集。recordItemId在自定義頁面/彈窗中顯式指定 idShow默認從路由讀取id信息。當組件被用在自定義頁面、Modal 或 Drawer 中無法從 URL 讀取 id 時就需要用recordItemId顯式傳入import { Show, useModalForm } from refinedev/antd; import { Modal, Button } from antd; const PostShow: React.FC () { const { modalProps, id, show } useModalForm({ action: show, }); return ( div Button onClick{() show()}Show Button/Button Modal {...modalProps} Show recordItemId{id} pRest of your page here/p /Show /Modal /div ); };源碼中的取值邏輯是const id recordItemId ?? idFromParams;index.tsxrecordItemId優先級高于路由參數。同時文檔特別提示Show需要id信息才能讓RefreshButton正常工作因為刷新按鈕會攜帶recordItemId重新觸發數據查詢。dataProviderName指定多數據提供者中的某一個不指定時Refine 會使用默認的 data provider。如果應用配置了多個 data provider可以通過dataProviderName指定使用哪一個import { Refine } from refinedev/core; import dataProvider from refinedev/simple-rest; import { Show } from refinedev/antd; const PostShow () { return Show dataProviderNameother.../Show; }; export const App: React.FC () { return ( Refine dataProvider{{ default: dataProvider(https://api.fake-rest.refine.dev/), other: dataProvider(https://other-api.fake-rest.refine.dev/), }} {/* ... */} /Refine ); };源碼中該屬性會被透傳給刪除按鈕與刷新按鈕見 index.tsx 與 L114-L119保證刪除與刷新請求也走同一個 data provider。goBack定制或禁用返回按鈕通過goBack屬性可以自定義返回按鈕也可以傳入false/空值來禁用import { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { const BackButton () Button←/Button; return ( Show goBack{BackButton /} pRest of your page here/p /Show ); };源碼中的goBack會被直接作為PageHeader的backIconindex.tsx而onBack只有在當前action不是list且已定義時才綁定useBack()返回的跳轉函數。因此有一個重要細節如果路由中沒有:action參數、或 action 是list即使傳了goBack也不會顯示返回按鈕因為onBack為 undefined 時 Ant Design 的 PageHeader 默認不渲染返回圖標。此時可以通過headerProps覆蓋onBack來強制啟用import { useBack } from refinedev/core; import { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { const back useBack(); const BackButton () Button←/Button; return ( Show goBack{BackButton /} headerProps{{ onBack: back }} pRest of your page here/p /Show ); };goBack的默認值是ArrowLeft /圖標類型為ReactNode見文檔末尾的 API Reference 表格。isLoading加載態由于Show內部使用 Ant Design 的Card組件isLoading可以直接傳入。為true時內容區會被Spin包裹顯示加載動畫import { Show } from refinedev/antd; const PostShow: React.FC () { return ( Show isLoading{true} pRest of your page here/p /Show ); };源碼中isLoading的默認值是falseindex.tsx并且它同時會禁用編輯/刪除/刷新按鈕...(isLoading ? { disabled: true } : {})避免加載過程中觸發重復操作。實踐中通常直接透傳useShow返回的query.isLoading。breadcrumb定制或禁用面包屑breadcrumb屬性用于定制面包屑不傳時默認使用refinedev/antd包導出的Breadcrumb組件。傳false可完全禁用import { Show, Breadcrumb } from refinedev/antd; const PostShow: React.FC () { return ( Show breadcrumb{ div style{{ padding: 3px 6px, border: 2px dashed cornflowerblue, }} Breadcrumb / /div } pRest of your page here/p /Show ); };源碼的優先級邏輯是index.tsx組件級breadcrumb優先若為undefined則回退到Refine/全局配置的options.breadcrumb都未提供時才渲染默認Breadcrumb /。Breadcrumb組件的詳細用法見 Breadcrumb 文檔。wrapperProps定制最外層容器refinedev/antd的 wrapper 元素就是普通的div/因此wrapperProps可以接收div/能接收的一切屬性className、style、id、事件等import { Show } from refinedev/antd; const PostShow: React.FC () { return ( Show wrapperProps{{ style: { backgroundColor: cornflowerblue, padding: 16px, }, }} pRest of your page here/p /Show ); };headerProps定制頁頭headerProps用于定制PageHeader。除了樣式還可以設置subTitle等 PageHeader 專屬屬性其類型為PageHeaderProps參考 packages/antd/src/components/crud/types.tsimport { Show } from refinedev/antd; const PostShow: React.FC () { return ( Show headerProps{{ subTitle: This is a subtitle, style: { backgroundColor: cornflowerblue, padding: 16px, }, }} pRest of your page here/p /Show ); };源碼通過{...(headerProps ?? {})}展開到PageHeader上因此你可以在其中覆蓋onBack、title等任何 PageHeader 屬性這正是上一節強制顯示返回按鈕技巧的原理。更多屬性可參考 ProComponents PageHeader 文檔。contentProps定制內容卡片contentProps用于定制包裹內容的Card類型為CardPropsimport { Show } from refinedev/antd; const PostShow: React.FC () { return ( Show contentProps{{ style: { backgroundColor: cornflowerblue, padding: 16px, }, }} pRest of your page here/p /Show ); };注意源碼中Card使用variantborderless的無邊框樣式index.tsxcontentProps會展開到該Card上因此你也可以通過它覆蓋actions即頁腳按鈕區。headerButtons定制頁頭按鈕默認情況下Show/的頁頭包含四個按鈕ListButton—— 返回列表頁EditButton—— 跳轉編輯頁DeleteButton—— 刪除當前記錄RefreshButton—— 刷新數據headerButtons接受React.ReactNode或渲染函數渲染函數的簽名是({ defaultButtons, listButtonProps, editButtonProps, deleteButtonProps, refreshButtonProps }) React.ReactNode方式一保留默認按鈕并追加自定義按鈕import { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { return ( Show headerButtons{({ defaultButtons }) ( {defaultButtons} Button typeprimaryCustom Button/Button / )} pRest of your page here/p /Show ); };方式二完全自定義按鈕組同時利用渲染函數提供的默認 props 復用各按鈕的默認值import { Show, ListButton, EditButton, DeleteButton, RefreshButton, } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { return ( Show headerButtons{({ deleteButtonProps, editButtonProps, listButtonProps, refreshButtonProps, }) ( Button typeprimaryCustom Button/Button {listButtonProps ( ListButton {...listButtonProps} meta{{ foo: bar }} / )} {editButtonProps ( EditButton {...editButtonProps} meta{{ foo: bar }} / )} {deleteButtonProps ( DeleteButton {...deleteButtonProps} meta{{ foo: bar }} / )} RefreshButton {...refreshButtonProps} meta{{ foo: bar }} / / )} pRest of your page here/p /Show ); };有幾個按條件渲染的規則需要記住這些行為同樣在 packages/antd/src/components/crud/show/index.spec.tsx 的測試中驗證過如果資源沒有定義listListButton不會渲染listButtonProps為undefined如果canDelete為falseDeleteButton不會渲染deleteButtonProps為undefined如果canEdit為falseEditButton不會渲染editButtonProps為undefinedRefreshButton始終渲染。所以在自定義渲染函數里務必用條件判斷包裹listButtonProps、editButtonProps、deleteButtonProps如上例避免解構到 undefined。各按鈕的詳細文檔ListButton、RefreshButton、EditButton、DeleteButton。headerButtonProps定制頁頭按鈕容器頁頭按鈕默認被包裹在一個 Ant DesignSpace組件里源碼 index.tsx。headerButtonProps類型為SpaceProps用于定制這個容器import { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { return ( Show headerButtonProps{{ style: { backgroundColor: cornflowerblue, padding: 16px, }, }} headerButtons{Button typeprimaryCustom Button/Button} pRest of your page here/p /Show ); };footerButtons定制頁腳按鈕footerButtons用于定制頁腳按鈕同樣接受ReactNode或渲染函數({ defaultButtons }) React.ReactNodeimport { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { return ( Show footerButtons{({ defaultButtons }) ( {defaultButtons} Button typeprimaryCustom Button/Button / )} pRest of your page here/p /Show ); };從源碼看index.tsx頁腳按鈕會作為Card的actions數組中的唯一一項渲染Card 的 actions 本身是一個數組這里把整個Space作為一個 action渲染函數的defaultButtons固定為null。footerButtonProps定制頁腳按鈕容器footerButtonProps用于定制頁腳按鈕的Space容器import { Show } from refinedev/antd; import { Button } from antd; const PostShow: React.FC () { return ( Show footerButtons{({ defaultButtons }) ( {defaultButtons} Button typeprimaryCustom Button/Button / )} footerButtonProps{{ style: { float: right, marginRight: 24, backgroundColor: cornflowerblue, padding: 16px, }, }} pRest of your page here/p /Show ); };源碼中的按鈕組裝邏輯把上述屬性串起來Show的核心工作就是在渲染前計算好四個按鈕的 propsindex.tsx按鈕生成條件關鍵 propsListButtonresource?.list存在且未傳recordItemIdresource: identifierEditButtoncanEdit ?? !!resource?.edit為真type: primary、recordItemId: id加載中時disabledDeleteButtoncanDelete ?? resource?.meta?.canDelete ?? deleteButtonProps為真recordItemId: id、dataProviderName、刪除成功回調go({ to: goListPath })RefreshButton始終渲染recordItemId: id、dataProviderName加載中時disabledhasList resource?.list !recordItemId這個條件很有意思當你在 Modal 里用recordItemId展示詳情時列表按鈕會被自動隱藏因為此時跳回列表頁沒有意義該行為在 index.spec.tsx 中有專門的測試用例should render optional recordItemId with resource prop, not render list button。測試保障共享 CRUD 測試套件refinedev/antd的Show不僅有自己的測試packages/antd/src/components/crud/show/index.spec.tsx還通過crudShowTests.bind(this)(Show)繼承了refinedev/ui-tests包中的共享 CRUD 測試套件packages/ui-tests/src/tests/crud/show.tsx。這套共享用例覆蓋了渲染 children、默認渲染編輯/刪除按鈕、按鈕可見性與權限聯動等跨 UI 框架的通用行為確保 Ant Design、MUI、Mantine、Chakra UI 等不同適配層對Show的語義保持一致。測試中還通過RefineButtonTestIds.DeleteButton等 test id 斷言按鈕的存在與禁用狀態。API ReferenceProperties屬性類型默認值titleReactNodeShow 資源單數名resourcestring \| Resource從路由解析recordItemIdBaseKey路由中的:iddataProviderNamestring默認 data providercanDeletebooleanresource?.meta?.canDeletecanEditboolean!!resource?.editdeleteButtonPropsDeleteButtonProps—goBackReactNodeArrowLeft /isLoadingbooleanfalsebreadcrumbReactNode \| false全局 breadcrumb 或Breadcrumb /wrapperPropsdiv的 HTML 屬性—headerPropsPageHeaderProps—contentPropsCardProps—headerButtonsReactNode \| render functionListButton、EditButton、DeleteButton、RefreshButtonheaderButtonPropsSpaceProps—footerButtonsReactNode \| render function—footerButtonPropsSpaceProps—其中部分類型的底層定義可以進一步查看 packages/antd/src/components/crud/types.tsShowProps是由RefineCrudShowProps泛型實例化而來分別對應SpaceProps按鈕容器、HTMLAttributesHTMLDivElementwrapper、PageHeaderProps頁頭與CardProps內容卡。小結Show的設計哲學是布局與邏輯分離數據獲取交給useShow/useOne頁面骨架與操作入口交給Show。通過title、goBack、breadcrumb、headerButtons、footerButtons等屬性你可以在零業務代碼的前提下獲得一個帶標題、面包屑、返回/刷新/編輯/刪除按鈕的完整詳情頁遇到 Modal、Drawer、自定義頁面等特殊場景時recordItemId、resource、dataProviderName又能保證組件依然正確工作。理解其源碼中按鈕可見性的判定順序props 優先、資源 meta 兜底與id的解析優先級recordItemId ?? 路由id將幫助你在實際項目中精準地定制和排查詳情頁行為。【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refine創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考