直接用 flex + v-for 实现九宫格最稳,因 display: grid 在小程序端不兼容、h5 端 gap 行为不一致;需显式声明 flex-wrap、子项设 flex: 0 0 33.333%、box-sizing: border-box、padding 控制间隙,并补足数据至 3 的倍数。

直接用 flex + v-for 就能跑通,别碰 display: grid 或第三方 uni-grid 插件——小程序端基础库低于 2.17.0 时完全不识别,H5 端 gap 行为不一致,真机上容易塌成一列或错行。
为什么 flex-wrap 是唯一可靠起点
九宫格本质是每行固定 3 列的流式布局,flex-wrap: wrap 能自动换行,但漏掉它就会溢出容器。iOS 微信里常见“只显示前 3 个、后面全挤到第二行末尾”的问题,就是没加这句。
- 父容器必须显式声明
display: flex; flex-wrap: wrap; - 子项不能只写
flex: 1,要设flex: 0 0 calc(33.333% - 2 * 8rpx)(假设左右各 8rpx 间距) -
box-sizing: border-box必须加,否则 padding 会让实际宽度超 100%,最后一行只剩 1–2 个 - 间隙统一用 padding 控制,别用 margin——总宽超标会触发异常换行
数据长度不是 3 的倍数时怎么补空位
后端返回 7 张图,渲染后最后一行只有 1 个格子?这不是样式问题,是数据没对齐。空 view 占位会参与 flex 计算,导致宽度错乱;直接留空又断裂。正确做法是 JS 补足至 3 的倍数,再用 v-if 过滤掉空项。
- 在
computed里处理:gridList() { const padded = [...this.pics]; while (padded.length % 3) padded.push(null); return padded; } - 模板中:
v-for="(item, i) in gridList" :key="i",然后image外层加v-if="item" - 别用
Array(9 - pics.length).fill(null)拼数组——如果 pics 是响应式数组,这种写法不会触发更新
图片变形、拉伸、黑边的根本原因
不是分辨率低,是 image 的 mode 和包裹结构没配对。App 端 image 不响应 height,小程序里 scaleToFill 会把人像脸拉长,而 aspectFit 留白又破坏网格对齐。
- 外层
view必须设overflow: hidden,否则 App 端图片高度失控 -
image推荐mode="aspectFill",配合width: 100%; height: 100% - 别直接给
image设宽高,要靠父view的padding-bottom: 33.333%控制宽高比 - 若需左对齐(如微博),容器加
justify-content: flex-start,但前提是子项没硬占满整行
H5 和小程序路径处理不一致导致预览黑屏
点击第 3 张图预览黑屏?大概率是传了临时路径给 H5 —— uni.previewImage 在 H5 下只认完整 URL(https:// 或 /static/ 开头),而小程序和 App 可直用 tempFilePath。
- 过滤无效路径:
const validUrls = urls.filter(u => typeof u === 'string' && u.trim()) - H5 端必须先上传或转 CDN,拿到合法 URL 再构造
urls数组 -
current参数必须是urls中某一项的**完整字符串值**,不是索引。传urls[2],不是2 - 本地图必须写全路径:
/static/img/1.jpg,不能只写1.jpg
最易被忽略的是:所有补空逻辑、路径过滤、mode 配置都得在真机上逐端验证——H5 正常的小程序可能报错,App 不卡的小程序可能闪退。跨端一致性不在 CSS 里,在每一处路径拼接和数组操作中。











