- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-vue
🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜
导读
Flex 是 ant-design-vue 自4.0.7版本起提供的弹性布局组件,它基于 CSS Flexbox 封装,用于快速设置块级子元素之间的间距以及水平、垂直方向上的对齐方式。读完本篇,你将掌握 Flex 组件的全部 API 用法、与 Space 组件的选型区别、预设间距与自定义间距的底层实现,以及如何通过嵌套组合出复杂的企业级布局。
何时使用 Flex
根据官方文档(components/flex/index.zh-CN.md),Flex 组件适合两类典型场景:
- 设置元素之间的间距:通过
gap属性在主轴与交叉轴方向统一控制子元素间隙,无需为每个子元素手动添加 margin; - 设置各种水平、垂直对齐方式:通过
justify(主轴对齐)与align(交叉轴对齐)快速完成分布与对齐布局。
这两个场景覆盖了日常页面开发中最高频的布局诉求:工具条按钮排列、表单行内元素对齐、卡片内容分布、页面顶部导航等。
与 Space 组件的区别
很多开发者会混淆 Flex 与 Space 两个组件,二者的定位存在本质差异:
| 对比维度 | Space | Flex |
|---|---|---|
| 适用元素 | 内联(inline)元素 | 块级(block)元素 |
| 包裹节点 | 会为每一个子元素添加包裹元素用于内联对齐 | 本身不会添加任何包裹元素 |
| 典型场景 | 行、列中多个子元素的等距排列 | 垂直或水平方向上的子元素布局 |
| 灵活度 | 较低,偏向等距间距 | 更高,提供对齐、换行、伸缩等完整控制能力 |
简单来说:Space 适合"一排等距摆放",Flex 适合"需要精细控制对齐与换行的块级布局"。官方文档的原文表述为:"Flex 为块级元素提供间距,其本身不会添加包裹元素。适用于垂直或水平方向上的子元素布局,并提供了更多的灵活性和控制能力。"
API 一览
版本说明:Flex 组件自
ant-design-vue@4.0.7版本开始提供。Flex 组件默认行为在水平模式下为向上对齐(flex-start),在垂直模式下为拉伸对齐(stretch),你可以通过属性进行调整。
完整属性表如下(与官方文档一致):
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| vertical | flex 主轴的方向是否垂直,使用flex-direction: column | boolean | false |
| wrap | 设置元素单行显示还是多行显示 | 参考 flex-wrap | nowrap |
| justify | 设置元素在主轴方向上的对齐方式 | 参考 justify-content | normal |
| align | 设置元素在交叉轴方向上的对齐方式 | 参考 align-items | normal |
| flex | flex CSS 简写属性 | 参考 flex | normal |
| gap | 设置网格之间的间隙 | small|middle|large| string | number | - |
| component | 自定义元素类型 | Component | div |
属性详解与源码级实现
下面逐一深入每个属性的用法,并结合仓库源码(components/flex/interface.ts、components/flex/index.tsx)说明其底层原理。
vertical:主轴方向
vertical接受 boolean 类型,默认false。当设置为true时,组件会生成flex-direction: column样式,让子元素从上到下纵向排列。
<template> <a-flex vertical gap="middle"> <div>第一行</div> <div>第二行</div> <div>第三行</div> </a-flex> </template>从源码看,该属性同时影响 className 的生成:
// components/flex/index.tsx { [`${prefixCls.value}-vertical`]: props.vertical ?? ctxFlex?.value.vertical, }对应的样式在 components/flex/style/index.ts 中定义:
'&-vertical': { flexDirection: 'column', },值得注意:vertical的取值会与ConfigProvider提供的 flex 上下文(ctxFlex)做合并(??),即 ConfigProvider 层面的默认值优先级低于组件自身的显式传入。此外,在垂直模式下如果未显式设置align,组件会自动补充拉伸对齐样式(详见 components/flex/utils.ts):
alignCls[`${prefixCls}-align-stretch`] = !props.align && !!props.vertical;wrap:换行策略
wrap直接映射 CSS 的flex-wrap属性,默认值为nowrap。可选值在源码中被显式枚举(components/flex/utils.ts):
export const flexWrapValues = ['wrap', 'nowrap', 'wrap-reverse'] as const;nowrap:所有子元素单行显示,必要时压缩自身尺寸(默认);wrap:一行放不下时自动换行;wrap-reverse:换行方向反转,多行时首行出现在下方。
<template> <a-flex wrap="wrap" gap="small"> <a-button v-for="item in 24" :key="item" type="primary">Button</a-button> </a-flex> </template>justify:主轴对齐
justify映射 CSS 的justify-content属性,控制子元素在主轴(水平模式为横向、垂直模式为纵向)上的分布方式。源码枚举了全部可选值(components/flex/utils.ts):
export const justifyContentValues = [ 'flex-start', 'flex-end', 'start', 'end', 'center', 'space-between', 'space-around', 'space-evenly', 'stretch', 'normal', 'left', 'right', ] as const;其中space-between(两端对齐)、space-around(两侧均匀分布)、space-evenly(含两端完全等距)是最常用的三种分布方式。组件通过生成${prefixCls}-justify-${value}类名,再交由样式钩子生成对应 CSS:
// components/flex/style/index.ts const genJustifyContentStyle: GenerateStyle<FlexToken> = token => { const justifyStyle: CSSInterpolation = {}; justifyContentValues.forEach(value => { justifyStyle[`${componentCls}-justify-${value}`] = { justifyContent: value }; }); return justifyStyle; };align:交叉轴对齐
align映射 CSS 的align-items属性,控制子元素在交叉轴方向的对齐。源码枚举的可选值(components/flex/utils.ts):
export const alignItemsValues = [ 'center', 'start', 'end', 'flex-start', 'flex-end', 'self-start', 'self-end', 'baseline', 'normal', 'stretch', ] as const;结合vertical使用时逻辑如下:垂直模式下未传align时自动应用stretch(拉伸),传了则按传入值生效。这一点在测试用例中有明确验证(components/flex/tests/index.test.js):
it('when vertical=true should stretch work', () => { const wrapper = mount({ render: () => <Flex vertical>test</Flex> }); const wrapper2 = mount({ render: () => <Flex vertical align="center">test</Flex> }); expect(wrapper.find('.ant-flex-align-stretch')).toBeTruthy(); expect(wrapper2.find('.ant-flex-align-center')).toBeTruthy(); });flex:子元素伸缩简写
flex属性映射 CSS 的flex简写属性,支持 number 或 string 类型(如1、1 1 auto、auto、1 0 100px等)。从源码看,该属性不是通过类名,而是直接以内联样式写入目标元素(components/flex/index.tsx):
if (flex) { mergedStyle.flex = flex; }当某个子元素需要"撑满剩余空间"时,直接给对应的 Flex 实例传flex="1"即可,非常适用于固定宽度 + 自适应宽度混排的布局。
gap:间距控制
gap是 Flex 组件最常用的属性,支持两种形态:
- 预设尺寸:
small/middle/large,对应主题 token 中的三档间距; - 自定义尺寸:string(如
'16px'、'1rem')或 number(如16,会被转换为16px)。
源码通过 components/_util/gapSize.ts 中的isPresetSize判断是否预设尺寸:
export function isPresetSize(size?: SizeType | string | number): size is SizeType { return ['small', 'middle', 'large'].includes(size as string); }- 若是预设尺寸,生成
${prefixCls}-gap-${gap}类名,由样式钩子映射到主题 token(components/flex/style/index.ts):
const flexToken = mergeToken<FlexToken>(token, { flexGapSM: token.paddingXS, // small → paddingXS flexGap: token.padding, // middle → padding flexGapLG: token.paddingLG, // large → paddingLG }); // '&-gap-small' { gap: token.flexGapSM } ...- 若是自定义值,则直接以内联样式写入
gap: ${gap}px:
if (gap && !isPresetSize(gap)) { mergedStyle.gap = `${gap}px`; }该行为在测试中亦有覆盖:
expect(wrapper2.element.style.gap).toBe('100px'); // <Flex gap={100}>component:自定义元素类型
默认渲染为div,传入其他组件或标签(如span、section、ul、甚至是自定义组件)时会按指定类型渲染。测试用例验证了这一点:
expect(wrapper.find('.ant-flex').element.tagName).toBe('DIV'); expect(wrapper2.find('.ant-flex').element.tagName).toBe('SPAN'); // <Flex component="span">这在需要语义化标签(如导航用nav、列表用ul)或复用自定义组件的场景下非常有用。注意源码中使用component: Component = 'div'的解构默认值,并会对justify、wrap、align、vertical等布局属性做omit,避免它们被透传到真实 DOM 上:
{...omit(othersProps, ['justify', 'wrap', 'align', 'vertical'])}实际使用示例
基本布局:水平 / 垂直切换
参考官方示例 components/flex/demo/basic.vue:
<template> <a-flex gap="middle" vertical> <a-radio-group v-model:value="value"> <a-radio value="horizontal">horizontal</a-radio> <a-radio value="vertical">vertical</a-radio> </a-radio-group> <a-flex :vertical="value === 'vertical'"> <div v-for="(item, index) in new Array(4)" :key="item" :style="{ width: '25%', height: '54px', background: `${index % 2 ? '#1677ff' : '#1677ffbf'}` }" /> </a-flex> </a-flex> </template> <script setup lang="ts"> import { ref } from 'vue'; const value = ref('horizontal'); </script>对齐方式组合
参考 components/flex/demo/align.vue,将justify与align组合使用即可实现九宫格级别的对齐控制:
<template> <a-flex gap="middle" align="start" vertical> <a-segmented v-model:value="justify" :options="justifyOptions" /> <a-segmented v-model:value="alignItems" :options="alignOptions" /> <a-flex :style="{ width: '100%', height: '120px', border: '1px solid #40a9ff' }" :justify="justify" :align="alignItems"> <a-button type="primary">Primary</a-button> <a-button type="primary">Primary</a-button> <a-button type="primary">Primary</a-button> <a-button type="primary">Primary</a-button> </a-flex> </a-flex> </template>justify常用值:flex-start、center、flex-end、space-between、space-around、space-evenly;align常用值:flex-start、center、flex-end。
预设间距与自定义间距
参考 components/flex/demo/gap.vue,gap既支持三档预设,也支持用滑块实时传入数值:
<template> <a-flex :gap="gapSize !== 'customize' ? gapSize : customGapSize"> <a-button type="primary">Primary</a-button> <a-button>Default</a-button> <a-button type="dashed">Dashed</a-button> <a-button type="link">Link</a-button> </a-flex> </template>自动换行
参考 components/flex/demo/wrap.vue,当子元素数量多、宽度超过容器时配合wrap="wrap"自动换行:
<template> <a-flex wrap="wrap" gap="small"> <a-button v-for="item in new Array(24)" :key="item" type="primary">Button</a-button> </a-flex> </template>嵌套组合:实现复杂布局
Flex 组件支持任意层级嵌套。参考官方组合示例 components/flex/demo/combination.vue,外层justify="space-between"做左右分布,内层垂直 Flex 用align="flex-end"+justify="space-between"做"图片 + 标题 + 按钮"的卡片式布局:
<template> <a-card :style="{ width: '620px' }" :body-style="{ padding: 0, overflow: 'hidden' }"> <a-flex justify="space-between"> <img alt="avatar" src="..." :style="{ display: 'block', width: '273px' }" /> <a-flex vertical align="flex-end" justify="space-between" :style="{ padding: '32px' }"> <a-typography> <a-typography-title :level="3"> “antd is an enterprise-class UI design language and Vue UI library.” </a-typography-title> </a-typography> <a-button type="primary" href="https://antdv.com" target="_blank">Get Start</a-button> </a-flex> </a-flex> </a-card> </template>实现原理与注意事项
类名生成与样式注入
Flex 组件采用 ant-design-vue 标准的CSS-in-JS 样式方案:渲染时通过useStyle(prefixCls)获得wrapSSR包装函数与hashId,并结合 components/flex/utils.ts 中的createFlexClassNames批量生成ant-flex-wrap-*、ant-flex-align-*、ant-flex-justify-*类名。因此每个对齐/换行值最终都会落到类名上,便于主题定制与调试。
默认行为的隐藏细节
文档强调的"水平模式向上对齐、垂直模式拉伸对齐"并非空话,其实现位于 components/flex/utils.ts:
alignCls[`${prefixCls}-align-stretch`] = !props.align && !!props.vertical;即:垂直模式下若不显式传align,则自动应用stretch。这与 React 版 antd 的行为保持一致,能保证纵向布局时子元素默认撑满交叉轴宽度。如果你需要垂直布局中让子元素按内容宽度收缩,记得显式传入align="flex-start"等值。
与 ConfigProvider 的联动
Flex 组件通过useConfigContextInject()读取 ConfigProvider 的 flex 上下文(ctxFlex)与direction(rtl 支持),全局层面可统一设置默认的垂直方向;同时vertical属性采用props.vertical ?? ctxFlex?.value.vertical的合并策略,组件级属性优先于全局配置。
空容器自动隐藏
在 components/flex/style/index.ts 中有一个容易被忽略的细节:
'&:empty': { display: 'none', },当 Flex 容器没有任何子节点时会被自动隐藏,避免空容器在页面上留下多余空隙。
渲染层面的注意事项
- Flex 组件默认
inheritAttrs: false,外部传入的class与style会与内部生成类名合并后一并渲染到根元素,外部样式优先级与自定义能力都有保障; gap传0时会被isPresetSize与if (gap)双重判断忽略,因为 CSSgap默认值就是 0,无需额外写入内联样式(参见 components/_util/gapSize.ts 的注释说明);- 需要语义化标签(
nav、section、ul等)或自定义组件作为容器时,使用component属性即可,但需注意布局相关 props 不会透传到 DOM。
总结
Flex 组件以极小的 API 面(7 个属性)覆盖了 Flexbox 布局的核心能力:gap解决间距、justify/align解决对齐、wrap解决换行、flex解决伸缩、component解决语义化渲染,并天然支持与 ConfigProvider 联动与 rtl 场景。对于需要精细控制块级元素布局的页面,优先选用 Flex 而非 Space;垂直模式记得留意其默认的stretch对齐行为。相关源码可继续深入阅读 components/flex/index.tsx、components/flex/utils.ts、components/flex/style/index.ts 及测试用例 components/flex/tests/index.test.js。
- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-vue
🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜
相关推荐
Ant Design Flex 组件完全指南:弹性布局容器的 API、源码原理与实战用法
Ant Design Flex 组件完全指南:弹性布局容器的 API、源码原理与实战用法 Flex 是 Ant Design(antd)自 5.10.0 版本开
前端UI组件设计系统Ant Design Space 组件完全指南:间距布局、对齐控制与 Space.Compact 紧凑组合
Ant Design Space 组件完全指南:间距布局、对齐控制与 Space.Compact 紧凑组合 本指南以 antd(Ant Design)核心布局组
前端UI组件设计系统ant-design-vue Space 组件完全指南:统一间距布局与 Space.Compact 紧凑组合
ant design vue Space 组件完全指南:统一间距布局与 Space.Compact 紧凑组合 ant design vue 中的 Space 组
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考