- 前端
- 小程序
- UI组件
- 移动开发
【免费下载链接】vant-weapp
轻量、可靠的小程序 UI 组件库
Layout 布局是 Vant Weapp 中最基础、最常用的布局方案,由van-row与van-col两个组件配合实现一套完整的 24 列栅格系统。本文以 packages/col/README.md 为核心,结合row、col的源码实现、WXS 计算逻辑与测试用例,系统讲解从组件引入、基本用法到span/offset/gutter参数细节,再到栅格宽度与间距的底层计算原理,帮助你完全掌握在小程序中快速搭建响应式行列布局的能力。
组件介绍与引入
什么是 van-row / van-col
Layout组件提供了van-row(行)和van-col(列)两个组件来进行行列布局,其设计思路与 Web 端常见的 24 列栅格系统一致:每一行被均分为 24 等份,列组件通过span属性声明自己占据的份数,从而组合出任意比例的多列布局。栅格系统同时支持列偏移(offset)与列间距(gutter),足以覆盖绝大多数列表、表单、卡片等页面的排版需求。
引入组件
在小程序中,组件使用前需要先完成注册。按 Vant Weapp 的标准方式,在页面或自定义组件的app.json(全局)或index.json(页面级)中通过usingComponents声明两个组件:
"usingComponents": { "van-row": "@vant/weapp/row/index", "van-col": "@vant/weapp/col/index" }完整的组件注册步骤可参考官方快速上手文档。引入后即可在 WXML 中直接使用<van-row>与<van-col>标签。
从仓库目录结构看,row与col作为两个独立组件包分别维护在 packages/row/ 与 packages/col/ 下,各自包含index.ts(逻辑)、index.wxml(模板)、index.wxs(样式计算)、index.less(样式)与index.json(组件声明),编译产物对应lib/row/与lib/col/。
基本用法:span 与 offset
通过 span 设置列宽
van-col提供span属性设置列元素宽度,取值表示该列在 24 列栅格中所占的份数。例如span="8"表示该列宽度为容器宽度的 8/24,即 1/3:
<van-row> <van-col span="8">span: 8</van-col> <van-col span="8">span: 8</van-col> <van-col span="8">span: 8</van-col> </van-row>通过 offset 设置偏移
offset属性用于设置列的偏移宽度,计算方式与span完全相同(偏移份数 × 容器宽度 / 24)。下面示例中,第二行第一列占span="4",第二列在span="10"的基础上再向左偏移offset="4";第三行则展示了纯偏移布局offset="12" span="12",让列恰好居中:
<van-row> <van-col span="4">span: 4</van-col> <van-col span="10" offset="4">offset: 4, span: 10</van-col> </van-row> <van-row> <van-col offset="12" span="12">offset: 12, span: 12</van-col> </van-row>span与offset相加不超过 24 时,各列可在同一行内并排展示;若相加超过 24,多出的列会自动折行,这与经典栅格系统的行为一致。
源码中的属性定义
在 packages/col/index.ts 中,col组件通过VantComponent声明了span与offset两个属性,类型均为Number:
VantComponent({ relation: useParent('row'), props: { span: Number, offset: Number, }, });可以看到col组件本身并不直接感知布局宽度,它通过relation: useParent('row')建立与父级row的关联关系(详见下文“父子组件关联机制”一节),真正决定列宽与偏移的样式类由模板和 LESS 样式共同生成。
模板中的类名拼接
在 packages/col/index.wxml 中,列根节点根据span与offset动态拼接类名:
<view class="custom-class {{ utils.bem('col', [span]) }} {{ offset ? 'van-col--offset-' + offset : '' }}" style="{{ computed.rootStyle({ gutter }) }}" > <slot /> </view>utils.bem('col', [span])基于 BEM 规范生成形如van-col--8的类名;offset存在时追加van-col--offset-4形式的类名;- 内联
style则由 WXS 模块computed.rootStyle({ gutter })根据间距动态计算(见下文“gutter 间距的底层实现”)。
24 列栅格样式的生成
在 packages/col/index.less 中,col的基础样式为左浮动布局,并通过 Less 递归循环一次性生成 24 档宽度与 24 档偏移:
.van-col { float: left; box-sizing: border-box; } .generate(24); .generate(@n, @i: 1) when (@i =< @n) { .van-col--@{i} { width: @i * (100% / 24); } .van-col--offset-@{i} { margin-left: @i * (100% / 24); } .generate(@n, (@i + 1)); }- 宽度类
.van-col--N的width为N × (100% / 24),即 N/24; - 偏移类
.van-col--offset-N通过margin-left: N × (100% / 24)实现左偏移; - 父级 packages/row/index.less 则通过
::after伪元素clear: both清除浮动,保证行容器能够正确包裹浮动的列。
这一实现决定了span与offset的可选值范围为 1~24 的整数,超出该范围的取值不会命中任何生成的类名。
设置列元素间距:gutter
基本用法
通过van-row的gutter属性可以设置列元素之间的间距,单位为 px,默认间距为 0。在行组件上声明一次,即可作用于其内部所有列:
<van-row gutter="20"> <van-col span="8">span: 8</van-col> <van-col span="8">span: 8</van-col> <van-col span="8">span: 8</van-col> </van-row>上例中三个span="8"的列之间会产生 20px 的均匀间距。仓库中的实际演示页面 packages/col/demo/index.wxml 也提供了相同的示例,配合 demo 目录下的深色/浅色色块(见 packages/col/demo/index.less)可以直观观察间距效果。
gutter 的响应式下发:row 的源码逻辑
gutter的传递并不依赖 WXML 逐列绑定,而是由row组件通过小程序的自定义组件 relations 机制,在子列接入时主动下发。见 packages/row/index.ts:
VantComponent({ relation: useChildren('col', function (target) { const { gutter } = this.data; if (gutter) { target.setData({ gutter }); } }), props: { gutter: { type: Number, observer: 'setGutter', }, }, methods: { setGutter() { this.children.forEach((col) => { col.setData(this.data); }); }, }, });这里有两个关键机制:
- 首次关联下发:
useChildren('col', ...)的回调在col子组件 linked(关联)时被触发,row读取自身data.gutter,若存在则通过target.setData({ gutter })写入子列; - 动态更新同步:
gutter属性配置了observer: 'setGutter',当运行中gutter值发生变化时,setGutter方法会把整份this.data(含最新gutter)同步给所有已关联的子列,保证动态修改间距时列间距即时刷新。
父子组件关联机制:useParent / useChildren
上述useParent('row')与useChildren('col', ...)均来自 packages/common/relation.ts,其本质是对小程序relations特性的封装:
useParent(name)声明组件与父级路径../{name}/index的ancestor(祖先)关系,并在created生命周期中通过getRelationNodes(path)[0]暴露只读的parent引用;useChildren(name, onEffect)声明组件与后代../{name}/index的descendant(后代)关系,在linked/linkChanged/unlinked三个时机回调onEffect,并暴露只读的children列表。
这套封装让row与col可以在不依赖selectComponent硬编码路径的前提下解耦协作:row只负责维护子列集合与分发gutter,col只负责消费从父级获取到的gutter并渲染自身宽度。
gutter 间距的底层实现:WXS 动态样式
间距并非通过固定 CSS 类实现,而是由 WXS 模块在渲染期动态计算内联样式。row的根节点样式见 packages/row/index.wxml:
<view class="van-row custom-class" style="{{ computed.rootStyle({ gutter }) }}"> <slot /> </view>其对应的计算逻辑在 packages/row/index.wxs:
function rootStyle(data) { if (!data.gutter) { return ''; } return style({ 'margin-right': addUnit(-data.gutter / 2), 'margin-left': addUnit(-data.gutter / 2), }); }而col侧对应的内联样式逻辑在 packages/col/index.wxs:
function rootStyle(data) { if (!data.gutter) { return ''; } return style({ 'padding-right': addUnit(data.gutter / 2), 'padding-left': addUnit(data.gutter / 2), }); }两者配合的核心思路是:
row通过负外边距(margin-left/margin-right取-gutter/2)向外扩展容器的可视宽度,抵消首尾列的内边距带来的缩进;- 每个
col通过内边距(padding-left/padding-right取gutter/2)在自身内部制造间距,从而让相邻列之间恰好呈现gutter的间隙,同时首列与容器左缘、末列与容器右缘不产生多余空白。
其中addUnit工具函数(见 packages/common/utils.ts)负责为数值追加px单位:数字类型转成"20px",字符串类型则原样保留(可直接传"20rpx"等带单位的字符串)。style函数则来自 packages/wxs/style.wxs,将键值对象序列化为内联样式字符串。
由于col已设置box-sizing: border-box,padding不会撑破由span决定的百分比宽度,栅格整体宽度依然严格等于容器宽度。
实际演示与测试验证
Demo 示例页
仓库为col提供了完整的演示页面 packages/col/demo/index.wxml,包含“基础用法”与“在列元素之间增加间距”两组示例,其中:
- 基础用法展示三行布局:等分三列(
span="8")、混合偏移(span="4"与span="10" offset="4")、居中布局(offset="12" span="12"); - 间距示例在
van-row上设置gutter="20",展示三列之间的 20px 间距效果。
演示页的组件注册见 packages/col/demo/index.json,色块样式定义见 packages/col/demo/index.less。
快照测试
col组件配有自动化测试 packages/col/test/demo.spec.ts,通过miniprogram-simulate加载 demo 页面并断言渲染结果与快照一致:
test('should render demo and match snapshot', () => { const id = simulate.load(path.resolve(__dirname, '../demo/index'), { rootPath: path.resolve(__dirname, '../../'), }); const comp = simulate.render(id); comp.attach(document.createElement('parent-wrapper')); expect(comp.toJSON()).toMatchSnapshot(); });该测试验证了 demo 页面(含span、offset、gutter各场景)在模拟环境中的稳定渲染,说明栅格类名拼接、WXS 内联样式计算与父子组件关联在运行时行为正确。
API 一览
Row Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| gutter | 列元素之间的间距(单位为 px) | string | number | - |
说明:
gutter声明类型为Number(见 packages/row/index.ts),WXS 层的addUnit亦支持传入带单位的字符串(如"20rpx"),实际使用时两种写法均可。
Col Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| span | 列元素宽度 | string | number | - |
| offset | 列元素偏移距离 | string | number | - |
说明:
span与offset在源码中声明为Number类型(见 packages/col/index.ts),推荐取值范围为 1~24 的整数,超出范围的取值不会命中 packages/col/index.less 中生成的栅格类。
外部样式类
| 类名 | 说明 |
|---|---|
| custom-class | 根节点样式类 |
van-row与van-col的根节点都预留了custom-class,可通过传入自定义样式类覆盖根节点样式(例如为列设置背景色、圆角或内边距等),便于在不侵入组件内部样式的前提下完成视觉定制。
小结
Vant Weapp 的 Layout 布局组件以 24 列栅格为骨架,通过van-row+van-col的组合即可快速搭建等分列、偏移列与带间距的多列布局。其工程实现颇具代表性:col仅声明span/offset两个属性,宽高由 Less 递归生成的 24 档类名驱动;gutter借助relations机制由row统一下发并由 WXS 动态计算正负内/外边距;整个布局不依赖 JS 布局引擎,纯模板 + 样式即可完成,兼具性能与可维护性。掌握这些参数与底层原理后,你可以轻松在小程序页面中实现任意比例的行列排版。
- 前端
- 小程序
- UI组件
- 移动开发
【免费下载链接】vant-weapp
轻量、可靠的小程序 UI 组件库
相关推荐
Vant 移动端栅格布局实战:van-row 与 van-col 的 24 栅格体系、间距算法与源码原理解析
Vant 移动端栅格布局实战:van row 与 van col 的 24 栅格体系、间距算法与源码原理解析 Vant 的 Layout 布局组件由 van r
前端UI组件Vant 移动端 Layout 布局组件完全指南:Row / Col 24 栅格系统的配置、源码原理与实战
Vant 移动端 Layout 布局组件完全指南:Row / Col 24 栅格系统的配置、源码原理与实战 导读 Vant 是基于 Vue 3 的移动端组件库,
前端UI组件Element UI Layout 布局完全指南:24 栅格系统(el-row / el-col)原理与实战
Element UI Layout 布局完全指南:24 栅格系统(el row / el col)原理与实战 Element UI 的 Layout 布局组件基
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考