Ant Design Drawer 基础抽屉实战:从右侧滑出的受控面板与完整 API 解析
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
抽屉(Drawer)是 Ant Design 中用于承载临时任务与附加内容的浮层组件。本文以仓库中的「基础抽屉」官方示例(components/drawer/demo/basic-right.md 及其配套代码 components/drawer/demo/basic-right.tsx)为骨架,讲解如何实现"点击按钮抽屉从右滑出、点击遮罩区关闭"这一最典型的应用场景,并向下深挖组件源码、测试用例与全部 API 参数,帮助你从"能跑通示例"进阶到"理解抽屉的完整行为模型"。
基础抽屉:最小可运行实现
官方示例的核心语义是:基础抽屉,点击触发按钮抽屉从右滑出,点击遮罩区关闭。完整代码如下(摘自 basic-right.tsx):
import React, { useState } from 'react'; import { Button, Drawer } from 'antd'; const App: React.FC = () => { const [open, setOpen] = useState(false); const showDrawer = () => { setOpen(true); }; const onClose = () => { setOpen(false); }; return ( <> <Button type="primary" onClick={showDrawer}> Open </Button> <Drawer title="Basic Drawer" onClose={onClose} open={open}> <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Drawer> </> ); }; export default App;这个示例虽然短小,却覆盖了 Drawer 的核心使用范式,可拆解为三个要点:
- 受控开关:
open属性决定抽屉是否可见,由useState(false)维护。点击按钮调用showDrawer将open置为true,抽屉即从右侧滑出。 - 关闭回调:
onClose在"点击遮罩层、点击关闭图标或按 Esc"等场景被触发,回调中把open置回false。onClose只负责"通知",真正关闭仍需你同步状态,这是典型的受控组件设计。 - 内容区:
title渲染标题栏,children(三个<p>占位内容)渲染在面板主体区域。
从 v5 开始,控制开关的受控属性统一为
open。v4 时代的visible已被标记为 deprecated(@deprecated Please use open instead),afterVisibleChange亦被afterOpenChange取代,见 components/drawer/index.tsx 的类型定义。
点击遮罩关闭与 Esc 关闭的行为模型
示例描述"点击遮罩区关闭"并非魔法,而是由两个默认值驱动的:
mask(默认true):是否展示遮罩层。示例未显式传入,因此默认有半透明遮罩。maskClosable(默认true):点击蒙层是否允许关闭。示例未显式传入,因此点击遮罩会触发onClose。keyboard(默认true):是否支持键盘Esc关闭。
也就是说,基础示例"点击遮罩区关闭"完全来自默认参数;如果你希望遮罩不可点击关闭,只需显式设置maskClosable={false}。需要无遮罩模式时可参考仓库中的 no-mask.md 演示(对应源码中'no-mask': !mask的类名拼接,见 components/drawer/index.tsx)。
滑出方向与尺寸:默认从右、默认 378px
示例标题为"基础抽屉",之所以"从右滑出",是因为placement的默认值为right。可取值有top/right/bottom/left,官方另提供 placement.md 演示用Radio.Group动态切换四个方向:
const [placement, setPlacement] = useState<DrawerProps['placement']>('left'); // ... <Drawer title="Basic Drawer" placement={placement} closable={false} onClose={onClose} open={open}>方向与尺寸的搭配规则如下:
right/left时使用width控制宽度;top/bottom时使用height控制高度。width默认378,height默认378(单位像素,也支持字符串如'50%')。- 预设尺寸
size:'default'(378px)或'large'(736px),优先级低于显式传入的width/height。
这一合并逻辑在源码中有清晰的体现(components/drawer/index.tsx):
const mergedWidth = React.useMemo<string | number>( () => width ?? (size === 'large' ? 736 : 378), [width, size], ); const mergedHeight = React.useMemo<string | number>( () => height ?? (size === 'large' ? 736 : 378), [height, size], );即:显式width/height优先,未传时按size取large: 736或default: 378。预设宽度的演示见 size.md。
Drawer 完整 API 参数速查
下表完整继承自官方文档 components/drawer/index.zh-CN.md,并结合源码补充了默认值与版本说明:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| autoFocus | 抽屉展开后是否将焦点切换至其 DOM 节点 | boolean | true | 4.17.0 |
| afterOpenChange | 切换抽屉时动画结束后的回调 | function(open) | - | |
| className | Drawer 容器外层 className 设置,如需设置最外层请使用 rootClassName | string | - | |
| classNames | 语义化结构 className | Record<SemanticDOM, string> | - | 5.10.0 |
| closeIcon | 自定义关闭图标;5.7.0 起设置为null或false可隐藏关闭按钮 | ReactNode | <CloseOutlined /> | |
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | |
| extra | 抽屉右上角的操作区域 | ReactNode | - | 4.17.0 |
| footer | 抽屉的页脚 | ReactNode | - | |
| forceRender | 预渲染 Drawer 内元素 | boolean | false | |
| getContainer | 指定 Drawer 挂载的节点并在容器内展现,false为挂载在当前位置 | HTMLElement | () => HTMLElement | Selectors | false | body | |
| height | 高度,placement为top或bottom时使用 | string | number | 378 | |
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
| mask | 是否展示遮罩 | boolean | true | |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | |
| placement | 抽屉的方向 | top|right|bottom|left | right | |
| push | 多层 Drawer 的推动行为 | boolean | { distance: string | number } | { distance: 180 } | 4.5.0+ |
| rootStyle | 最外层容器样式,与style的区别是作用节点包括mask | CSSProperties | - | |
| size | 预设抽屉宽度(或高度),default 378px / large 736px | 'default' | 'large' | 'default' | 4.17.0 |
| style | Drawer 容器样式,仅需设置内容部分请使用bodyStyle | CSSProperties | - | |
| styles | 语义化结构 style | Record<SemanticDOM, CSSProperties> | - | 5.10.0 |
| title | 标题 | ReactNode | - | |
| loading | 显示骨架屏 | boolean | false | 5.17.0 |
| open | Drawer 是否可见 | boolean | - | |
| width | 宽度 | string | number | 378 | |
| zIndex | 设置 Drawer 的 z-index | number | 1000 | |
| onClose | 点击遮罩层、关闭图标或取消按钮时的回调 | function(e) | - | |
| drawerRender | 自定义渲染抽屉 | (node: ReactNode) => ReactNode | - | 5.18.0 |
关于loading属性有一处官方明确的演进记录:自5.17.0提供loading后,5.18.0修复了设计失误,将内置的 Spin 组件替换为 Skeleton 组件,同时收窄了loading的类型范围(仅接收 boolean)。在源码 DrawerPanel.tsx 中可以看到,loading为true时 body 内渲染的是 5 行 paragraph 的Skeleton:
{loading ? ( <Skeleton active title={false} paragraph={{ rows: 5 }} className={`${prefixCls}-body-skeleton`} /> ) : ( children )}面板结构:header / body / footer 三段式
抽屉内容面板由 DrawerPanel.tsx 组装,结构固定为三段:
- header:当
title或关闭按钮存在时渲染,包含关闭图标(closeIcon,默认<CloseOutlined />,经useClosable合并)、标题与右侧extra操作区;仅有关闭按钮而无标题、无 extra 时会附加-header-close-only类(见 DrawerPanel.tsx)。 - body:主体内容区,默认渲染
children,loading时替换为 Skeleton。 - footer:仅当传入
footer属性时渲染页脚节点。
三个区域都支持通过classNames(语义化 className)与styles(语义化 style)精确控制样式,这正是 5.10.0 引入的 Semantic DOM 能力,示例见 classNames.md。另注意官方提示:v5 使用rootClassName与rootStyle配置最外层元素样式,v4 的className/style语义改为作用于 Drawer 窗体本身,以与 Modal 对齐。
源码级原理:尺寸合并、动画、push 与 zIndex
在 components/drawer/index.tsx 中,可以观察到Drawer是对rc-drawer的封装,几个值得注意的实现细节:
- 动画配置:
maskMotion与panelMotion均设置了motionAppear / motionEnter / motionLeave且motionDeadline: 500,其中面板动画按placement区分(panel-motion-${motionPlacement}),这解释了"从右滑出"的滑入动画来源(index.tsx)。 - 多层抽屉推动:
push默认值为{ distance: 180 }(defaultPushState),当存在多层 Drawer 时,下层会被向同方向推动 180px,演示见 multi-level-drawer.md。 - zIndex 管理:通过
useZIndex('Drawer', rest.zIndex)获取层级,并与zIndexContext.Provider联动,保证 Drawer、Modal、Popover 等浮层之间的遮挡顺序正确。 - ContextIsolator:渲染时用
<ContextIsolator form space>隔离 Form 与 Space 上下文,避免抽屉内容意外继承外层表单行为。 - 废弃属性告警:开发环境下会对
visible、afterVisibleChange、headerStyle、bodyStyle、contentWrapperStyle、maskStyle、drawerStyle等旧属性逐一发出deprecated警告,引导迁移到open、afterOpenChange、styles.*新写法(index.tsx)。
测试如何验证"基础抽屉"
仓库为抽屉组件维护了多层测试,可作为行为契约参考:
- demo.test.ts 通过
demoTest('drawer')对所有 demo 做冒烟渲染,保证示例可运行。 - demo-extend.test.tsx 在 mock
rc-drawer(强制open: true、getContainer: false、禁用动画)后对 demo 进行更严格的交互测试。 - Drawer.test.tsx 覆盖了
render correctly、getContainer返回 undefined/false、render top drawer(placement="top"配height)、RTL 方向等核心行为,其中triggerMotion通过模拟遮罩与面板的animationEnd完成动画推进。
这些测试共同印证了示例中的行为:打开由open驱动、方向由placement决定、遮罩点击与 Esc 通过onClose通知状态更新。
从基础示例出发的下一步
基础抽屉解决的是"临时任务浮层"这一最小诉求。当业务场景变复杂时,官方演示集还提供了成套方案,均可在仓库中直接查看:
- 表单放入抽屉:form-in-drawer.md
- 信息预览型抽屉:user-profile.md
- 多层抽屉联动:multi-level-drawer.md
- 渲染在当前 DOM(
getContainer={false}):render-in-current.md - 预设宽度:size.md
掌握本文的基础范式(受控open+onClose回调 + 默认placement="right"+ 默认宽高 378px),再配合完整 API 表与源码实现细节,即可在项目中自如地驾驭这一"从屏幕边缘滑出的浮层面板"。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考