微信小程序:列表渲染——wx:for 嵌套与九九乘法表
一、前言小程序里的页面是数据驱动的视图只负责声明要渲染成什么样数据从逻辑层流向视图由 WXML 的渲染语法完成展示。当需要重复渲染一组结构相同的元素时靠手写九行重复代码显然不现实这时就要用到列表渲染语法wx:for。本文通过一个九九乘法表案例把wx:for的用法讲透用固定的[1,2,3,4,5,6,7,8,9]数组驱动双层循环外层循环控制行、内层循环控制列再用wx:if{{j i}}把矩形表格裁剪成下三角形态最后配合取余运算实现奇偶行换色。整个案例完全没有写 JavaScript 逻辑是纯粹的 WXML 渲染练习适合用来观察语法如何直接决定页面结构。二、核心概念wx:for 列表渲染写在组件上接收一个数组组件就会被重复渲染数组长度那么多次。案例里外层view用wx:for{{[1,2,3,4,5,6,7,8,9]}}渲染出 9 行内层再用同样的写法渲染 9 个格子。数组字面量{{[1,2,3,4,5,6,7,8,9]}}是在 WXML 的双花括号里直接写数组省去了在 js 的 data 中声明数据。九九乘法表的行列都是固定 9 个这样写最直接。wx:for-item 自定义循环变量名默认每一轮拿到的元素叫item。嵌套循环时如果内外层都用默认名内层会把外层的item覆盖掉外层的行号就取不到了所以必须用wx:for-itemi和wx:for-itemj分别命名。wx:for-index 自定义索引名默认索引叫index嵌套时同样有覆盖问题可用wx:for-index重命名。本案例的元素值本身就是 1~9直接当行号列号用因此没有用到索引。wx:if 条件渲染wx:if{{j i}}决定这个格子要不要渲染。内层固定循环 9 次只有当列号小于等于行号时才保留于是第 1 行 1 个格子、第 2 行 2 个格子逐行递增最终形成阶梯状的下三角。三元表达式拼接 classclassrow {{i%20 ? row-even : row-odd}}用行号对 2 取余的结果在三元运算符里二选一把类名拼到row后面实现奇偶行不同背景色。pageOrientation 页面方向页面 json 里配置pageOrientation: landscape让这个页面以横屏展示9 列格子才有足够的横向空间。flex 弹性布局行容器.row用flex-direction: row让格子横向排列外层.content用flex-direction: column让行纵向堆叠两行 CSS 就搭出了表格骨架。三、项目文件结构四、代码实现1. pages/index/index.wxml【结构页面】navigation-bar title九九乘法表 back{{false}} colorblack background#FFF/navigation-bar scroll-view classscrollarea scroll-y typelist view classbox view classtitle九九乘法口诀表/view view classcontent view classrow {{i%20 ? row-even : row-odd}} wx:for{{[1,2,3,4,5,6,7,8,9]}} wx:for-itemi view wx:for{{[1,2,3,4,5,6,7,8,9]}} wx:for-itemj view classitem wx:if{{j i}} {{j}}*{{i}}{{j*i}} /view /view /view /view /view /scroll-view2. pages/index/index.js【页面数据】// index.js Page({ })页面的逻辑层是空的。乘法表所需的行数、列数、乘积全部由 WXML 内部完成计算Page({})里没有声明任何 data这是本案例最值得注意的地方——渲染逻辑全部下沉到了视图层。3. pages/index/index.wxss【页面样式】/*index.wxss*/ page { height: 100vh; display: flex; flex-direction: column; } .scrollarea { flex: 1; overflow-y: hidden; } .content { display: flex; flex-direction: column; } .row { display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: start; } /* 偶数行背景色 */ .row-even { background-color: #ff9966; } /* 奇数行背景色 */ .row-odd { background-color: #87c8f5; } .item { width: 78rpx; /*作业扩展单元格边框*/ border: 2rpx solid blue; text-align: center; }4. pages/index/index.json【页面配置】{ usingComponents: { navigation-bar: /components/navigation-bar/navigation-bar }, pageOrientation: landscape }这里的pageOrientation是关键配置项取值landscape表示该页面强制横屏显示。5. app.wxss【全局样式】/**app.wxss**/ .box { margin: 20rpx; padding: 20rpx; border: 4rpx solid pink; } .title { font-size: 40rpx; font-weight: bolder; text-align: center; margin-bottom: 30rpx; color: red; }6. app.json【全局配置】{ pages: [ pages/index/index ], window: { navigationBarTextStyle: black, navigationStyle: custom }, style: v2, renderer: webview, componentFramework: glass-easel, sitemapLocation: sitemap.json, lazyCodeLoading: requiredComponents }五、运行效果页面以横屏方式打开顶部是自定义导航栏「九九乘法表」。内容区最外侧套着一圈粉色描边的框框内居中是红色加粗标题「九九乘法口诀表」。标题下方是一张九行九列的阶梯形表格第一行只有 1 个格子写着1*11第二行 2 个格子1*22、2*24以此类推到第九行 9 个格子最后一个格子是9*981。格子里的文字居中对齐每个格子的边框是蓝色细线横向紧挨着排列、不换行。整张表的奇数行第 1、3、5、7、9 行底色是浅蓝色偶数行第 2、4、6、8 行底色是橙色一深一浅交替出现行与行的边界看得很清楚。由于行背景色是铺满整行的右侧空白的部分也带着底色能直观看出这个位置本来有格子但被条件渲染过滤掉了。六、知识点总结1、wx:for的值是一个数组组件会按数组长度重复渲染。案例中两个wx:for都直接写了[1,2,3,4,5,6,7,8,9]这样的数组字面量好处是不用去 js 里准备数据缺点是数组长度写死在模板里想改成 19 行 19 列就必须同时改两处字面量。2、嵌套wx:for时必须用wx:for-item给内外层起不同的名字。如果内层不写wx:for-itemj那么内层的item会遮蔽外层的item{{j*i}}里就取不到行号了。案例把外层命名为i、内层命名为j正是为了在乘积表达式里同时引用两个变量。3、wx:for的默认变量名是item默认索引名是indexwx:for-item与wx:for-index就是用来改这两个名字的。改名不只是为了可读性在嵌套场景下它是让代码能正确运行的必要条件。4、wx:if{{j i}}的作用是渲染前过滤。内层循环每次都会完整跑 9 遍但只有列号不大于行号的格子才真正被创建。这种写法的计算量是 81 次循环、45 个节点如果改成在 js 里预先生成第 i 行有 i 个元素的二维数组循环次数会降到 45 次。数据量小时差别可以忽略但这是模板负责渲染、逻辑负责数据的分工问题。5、本例没有写wx:key。因为数组是固定的字面量、内容永远不变、也不会重排所以暂时看不出问题。但如果这个列表将来会动态增删缺少wx:key会让小程序在 diff 时按索引复用节点容易出现状态错位而且会给出性能警告。规范做法是配置唯一值例如wx:key*this元素本身唯一时或指向对象的某个唯一字段。6、奇偶行配色靠的是{{i%20 ? row-even : row-odd}}。三元表达式只能二选一取余结果为 0 走row-even否则走row-odd。写在class属性里的{{}}会和前面的静态类名row拼接最终得到row row-even或row row-odd这样的双类名。7、.item的宽度写的是78rpx9 个格子加起来 702rpx再叠加每个格子左右各 2rpx 的边框实际占用接近 738rpx。在 750rpx 的设计宽度内刚好放得下所以视觉上没有溢出。但只要把78rpx改成80rpx整行就会超出屏幕并被迫换行——固定宽度 边框叠加是这类表格最容易被忽略的布局细节。8、pageOrientation: landscape让页面横屏。需要注意的是rpx的换算规则是屏幕宽度固定为 750rpx横屏之后屏幕的宽度变成了物理长边同样写78rpx的格子会比竖屏时更大。也就是说横屏不是为了看起来更宽而是让 750rpx 这个基准落到更长的边上从而容纳下 9 列内容。9、相邻格子的边框会相互叠加两个相邻的2rpx边框贴在一起视觉上中间那条线是4rpx比外侧边框粗一倍。要消除这种粗细不均可以把边框改成只画右边和下边border-right/border-bottom或者给view加box-sizing: border-box后配合负外边距处理。10、页面纵向布局用的是page { height: 100vh; display: flex; flex-direction: column; }加.scrollarea { flex: 1; }。这样scroll-view会自动占满导航栏以外的剩余高度内容超长时在内部滚动不会把整个页面顶出去。这套骨架在内容可能超出屏幕的页面里可以通用。七、拓展思考wx:for是小程序里出现频率最高的渲染语法商品列表、消息列表、订单列表都靠它。把本例改造成真正的表格只需一步把写死的数组字面量换成 js 里data中的二维数组行数与列数就能自由配置19×19、9×9 只需改一个数字。再往后可以延伸出几种常见变体给表格加表头单独一行view用flex对齐列宽把格子做成可点击的方块bindtap携带data-row、data-col实现九宫格类游戏或者把乘法表换成班级课程表——本质上都是二维数据 双层循环 条件着色思路完全一样。