最稳方案是flex + v-for,显式声明flex-wrap、子项宽33.333%、box-sizing: border-box、padding代替margin、数据补空项防断裂。
直接用 flex + v-for 实现最稳,别碰 grid 或第三方 uni-grid 插件——小程序基础库低于 2.17.0 时 display: grid 直接不识别,h5 端 gap 行为在 ios 和安卓也不一致,补空、换行、点击热区等细节一动就崩。
为什么 flex-wrap 是唯一靠谱方案
微信朋友圈、美团分类、微博九宫格菜单,全靠 flex-wrap: wrap 底层机制撑住跨端一致性。它不依赖新 CSS 特性,所有平台(包括旧版安卓 WebView)都认。
-
flex: 0 0 calc(33.333% - 2 * 8rpx)比单纯写width: 33.333%更准——显式扣掉左右 padding,避免 box-sizing 失效导致总宽超 100% - 漏写
flex-wrap: wrap?子项会强行挤在一行溢出容器,iOS 微信里尤其明显 - 不用
margin控制间隙:父容器padding: 0 10rpx+ 子项padding: 20rpx 10rpx,避免 margin 折叠或跨端计算偏差
数据长度不是 3 的倍数怎么办
后端返回 7 条、8 条、11 条太常见,不处理就会最后一行只剩 1–2 个格子,视觉断裂。
- 补空必须用真实对象,比如
{ icon: '', text: '' },不能是null或undefined,否则v-for渲染报错 - 补空逻辑写在
computed里:gridList() { const padded = [...this.rawList]; while (padded.length % 3) padded.push({ icon: '', text: '' }); return padded; } - 不想显示空格子?加
v-if="item.icon",但得同步把子项宽度改成calc(100% / 3),并确保父容器仍带flex-wrap
图片糊、文字截断、点击没反馈怎么调
高频点击菜单的体验细节,往往卡在几个 CSS 属性和 image mode 上。
-
image必须包一层view并设固定宽高,mode="aspectFill"配合服务端裁剪图 URL,比前端硬缩更可控;禁用scaleToFill(拉伸失真)和aspectFit(留白破坏网格) - 文字用
text-overflow: ellipsis+white-space: nowrap,字号 ≤24rpx,避免在小屏上被截成“点检填…” - 加
hover-class,且设hover-start-time="50"+hover-stay-time="50",H5 和小程序都能有及时反馈
真正容易被忽略的是 box-sizing: border-box——不加它,padding 就会撑出实际宽度,三列变两列甚至一列,而且只在部分安卓机上复现,调试时极难定位。











