☰
微信小程序:条件渲染与 picker 选择器组件——wx:if 分支判断、block 分组与下拉选择的联动
2026/10/24 4:12:27 网站建设 项目流程

一、前言

小程序的页面结构并不是静态的:同样一块区域,可能要根据不同的数据状态显示不同内容;同样一组元素,可能要整体出现或整体消失。这就是"条件渲染"要解决的问题。微信小程序提供了 wx:if、wx:elif、wx:else 三个指令来组织分支,另外还有一个特殊的 block 标签,用来把多个节点打包成一个整体,让它统一受一个条件控制。

而 picker 是小程序里最常用的表单组件之一,它把"选择"这个交互从手写下拉框变成了系统原生的滚轮选择器,支持单列选择、多列选择、日期、时间、省市区等多种模式。picker 的取值方式与普通输入框不同:它不返回选项文本,而是返回选中项在数组中的下标,需要开发者自己用下标去数组里取真实值。

本文通过一个完整案例,把这两块内容串起来:用一个 picker 选择颜色,用 wx:if 分支链把下标翻译成中文颜色名,再用 style 动态绑定把选中的颜色直接渲染成色块;同时用一个输入框配合 block 分组,演示"一组元素同时显示/同时隐藏"的写法。

二、核心概念

wx:if 条件渲染:根据表达式的真假决定节点是否渲染。为 false 时节点根本不会被创建,为 true 时才创建并插入页面。

wx:elif 与 wx:else:与 wx:if 配合构成多分支,效果类似 JavaScript 的 else if / else。必须紧跟在 wx:if 或上一个 wx:elif 之后,中间不能插入其他节点,否则该分支失效。

block 标签:一个"包装标签",本身不会渲染成任何真实节点,只在编译阶段充当分组容器。它的作用是让多个元素共享同一个 wx:if 条件,避免给每个元素都重复写一遍判断。

picker 组件:小程序原生的选择器,点击后从底部弹出滚轮面板。案例使用 mode="selector",即普通单列选择器。

range 属性:指定选择器的数据源,需要传入一个数组。案例中传入colors数组,所以滚轮里显示的是 red、green、blue、purple 四项。

bind:change 事件:picker 的选择结果通过 change 事件回传,回调参数e.detail.value是选中项的下标(索引),而不是选中项的文本内容。

下标与真实值的对应关系:picker 只负责给出下标,真正的值要靠colors[color]这样的数组索引去取。这是 picker 与 input 最大的差别。

style 动态绑定:style="background-color: {{colors[color]}};"把数据拼接进内联样式,数据一变、背景色立即跟着变,是数据驱动视图的直接体现。

bind:blur 失焦事件:input 失去焦点时触发。案例用它读取输入框的数字,再决定 block 里的色块是否显示。

Number() 与 isNaN() 校验:Number('')得到 0,Number('abc')得到 NaN。用isNaN(num)判断转换是否成功,成功才写入 data,避免把无效值渲染到页面。

三、项目文件结构

条件渲染和picker组件/ ├── app.js 小程序逻辑,注册小程序实例 ├── app.json 全局配置:页面路径、窗口表现 ├── app.wxss 全局样式(.box、.title) ├── .eslintrc.js 代码规范配置 ├── sitemap.json 微信索引配置 ├── project.config.json 项目配置(appid、编译设置等) ├── project.private.config.json 项目私有配置 ├── components/ 自定义组件目录 │ └── navigation-bar/ 自定义导航栏组件(原生导航栏被关闭后用它替代) │ ├── navigation-bar.js │ ├── navigation-bar.json │ ├── navigation-bar.wxml │ └── navigation-bar.wxss └── pages/ 页面目录 └── index/ 首页 ├── index.js 页面逻辑:picker 取值、输入框数字校验 ├── index.json 页面配置:注册自定义组件 ├── index.wxml 页面结构:picker、分支链、block 分组 └── index.wxss 页面样式:picker 外观、色块尺寸

案例只有 index 一个页面,所以两个功能区块上下排在同一个 scroll-view 里。

四、代码实现

1. pages/index/index.wxml【结构页面】

<navigation-bar title="条件渲染和picker组件" back="{{false}}" color="black" background="#FFF"></navigation-bar> <scroll-view class="scrollarea" scroll-y type="list"> <view class="box"> <view class="title">条件渲染和picker组件</view> <picker mode="selector" range="{{colors}}" bind:change="colorPick"> 请点击此处的picker组件 </picker> <view wx:if="{{color == 0}}">红色</view> <view wx:elif="{{color == 1}}">绿色</view> <view wx:elif="{{color == 2}}">蓝色</view> <view wx:elif="{{color == 3}}">紫色</view> <view class="view-item" style="background-color: {{colors[color]}};"></view> </view> <view class="box"> <view class="title">block中的条件渲染</view> <input type="digit" placeholder="请输入一个数字" bind:blur="colorSet" /> <block wx:if="{{digitValue > 0}}"> <view class="view-item bc-red"></view> <view class="view-item bc-green"></view> <view class="view-item bc-blue"></view> <view class="view-item bc-purple"></view> </block> </view> </scroll-view>

结构上有三个看点:picker 的插槽内容(请点击此处的picker组件)就是点击热区;四个分支用 wx:if / wx:elif 串成一条链;第二个 box 里四个色块被一个 block 包住,共享digitValue > 0这一个条件。

2. pages/index/index.js【页面数据】

Page({ data: { color: 0, digitValue: 1, colors: ['red', 'green', 'blue', 'purple'] }, colorPick(e) { console.log(e); this.setData({ color: e.detail.value }) console.log(this.data.color); }, colorSet(e) { console.log(e); let num = Number(e.detail.value) if (!isNaN(num)) { this.setData({ digitValue: num }) } console.log(this.data.digitValue); } })

color初始为 0,所以页面一进来就已经显示"红色"和红底色块;digitValue初始为 1,所以第二个 box 的四个色块默认也全部显示。这两个初始值决定了首次打开页面时不会出错,是整个案例的"默认状态"。

3. pages/index/index.wxss【页面样式】

/*index.wxss*/ page { height: 100vh; display: flex; flex-direction: column; } .scrollarea { flex: 1; overflow-y: hidden; } picker { color: tomato; margin: 40rpx; background-color: lightgray; font-size: 40rpx; padding: 20rpx; } .view-item { width: 90%; height: 100rpx; margin: auto; } .bc-red { background-color: red; } .bc-green { background-color: green; } .bc-blue { background-color: blue; } /* 新增紫色样式 */ .bc-purple { background-color: purple; }

第一块色块的背景色由内联 style 决定,第二块由bc-*类决定,两种写法在一个页面里形成了对照。.view-item只给了宽 90%、高 100rpx、margin: auto水平居中,没有给纵向间距,所以多个色块是紧贴着排列的。

4. pages/index/index.json【页面配置】

{ "usingComponents": { "navigation-bar": "/components/navigation-bar/navigation-bar" } }

5. app.wxss【全局样式】

/**app.wxss**/ .box { margin: 20rpx; padding: 20rpx; border: 2rpx solid purple; text-align: center; } .title { font-size: 40rpx; font-weight: bolder; text-align: center; margin-bottom: 30rpx; color: red; }

.box和.title这两个类都写在全局样式里,页面 wxss 没有重新定义,所以两个功能区的紫色边框、红色标题、内容居中都来自这里。

6. app.json【全局配置】

{ "pages": [ "pages/index/index" ], "window": { "navigationBarTextStyle": "black", "navigationStyle": "custom" }, "style": "v2", "renderer": "webview", "componentFramework": "glass-easel", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents" }

关键项是"navigationStyle": "custom"——它把微信原生导航栏关掉了,页面顶部因此空出一整块安全区域,必须靠自定义的navigation-bar组件补上,组件内部用wx.getMenuButtonBoundingClientRect()获取胶囊按钮位置来对齐高度。

五、运行效果

页面顶部是自定义导航栏,标题「条件渲染和picker组件」,因为传了back="{{false}}",左侧没有返回箭头。

第一个紫色边框的盒子里,红色加粗标题下方是一个浅灰底、橙色文字的 picker 热区,写着「请点击此处的picker组件」。点击它,屏幕底部弹出滚轮面板,四行依次是 red、green、blue、purple。滚动到某一项并点确定后,热区文字会变成所选内容;同时下方立刻出现一行中文颜色名——选 red 显示"红色"、选 green 显示"绿色",依此类推。再往下是一条通栏色块,宽度为屏幕的 90%、高 100rpx,背景色随选择实时变化,选 red 就是红条,选 purple 就是紫条。

第二个紫色边框的盒子标题是「block中的条件渲染」,下面是一个提示"请输入一个数字"的输入框,点击后弹出带小数点的数字键盘。因为初始值digitValue为 1,页面刚加载时输入框下方就已经并排贴着四条色带,从上到下依次是红、绿、蓝、紫,每条都占 90% 宽、100rpx 高、水平居中。在输入框里输入一个正数并点空白处让它失焦,四条色带保持显示;输入 0 或者负数再失焦,四条色带会一起消失;重新输入正数又一起回来。这个"要么全在、要么全不在"的效果,正是 block 统一控制的结果。

六、知识点总结

1、wx:if为 false 时节点不会被创建,为 true 时才创建并插入页面,是真正的"增删节点"。这与 hidden 属性有本质区别:hidden只是把元素藏起来,节点始终存在于页面结构中,占位和事件绑定都还在。数据切换频繁时用 hidden 更省性能,数据很少变化、切换后希望彻底销毁组件状态时用 wx:if。

2、wx:elif和wx:else必须紧邻在wx:if或上一个wx:elif之后。如果在它们之间插入了别的节点(哪怕是一个注释之外的普通 view),后续分支就会失效,条件判断会断链。案例里四个分支是连续书写的,所以能正常工作。

3、block不是组件,页面渲染后不会产生任何真实节点,它只是在编译阶段起分组作用。案例中如果把 block 换成 view,会多出一层无意义的盒子,还可能继承.box之类的样式;换成四个独立的wx:if,则同一个条件要重复写四遍,改起来容易漏。block 正好解决了这两个问题。

4、picker 的 change 事件返回的e.detail.value是下标,不是文本。所以案例里拿到的是 0/1/2/3 这样的数字,要靠colors[color]才能取到 'red'。如果 range 传入的是对象数组(比如[{name:'红色',value:'red'}]),还必须额外配置range-key="name"才能正确显示,取值则要用list[index].value。

5、分支链里用了宽松相等color == 0而不是严格相等color === 0。官方文档标注 selector 的e.detail.value是数字类型,但部分基础库版本实际回传的是字符串(如"0")。这里用==恰好同时兜住了两种类型,属于"碰巧写对了":一旦改成===,在字符串回传的环境下四个分支会全部不成立,中文颜色名就整行消失,只剩下色块。更稳妥的写法是显式转换:const idx = Number(e.detail.value)。

6、picker 没有绑定value属性,所以第二次打开滚轮时它总是从第一项开始,而不是停在上次选中的位置。规范写法是加上value="{{color}}"把选中下标反向绑回去,形成闭环。案例里因为下方色块始终跟着走,用户还能判断当前状态,所以问题不明显;但在"选择后需要回显"的表单场景里这是必须补的。

7、第一块色块用内联 style、第二块用bc-*类,两种方式都有效,但优先级和可维护性不同:内联 style 的优先级最高,会覆盖 wxss 里同名属性,改样式时找不到来源;类名切换则便于统一管理、支持伪类和媒体查询。能用类名表达的状态尽量用类名,只有颜色值来自动态数据(如用户自选)时才用 style。

8、picker的插槽内容(请点击此处的picker组件)就是整个点击热区。picker 本身不会有任何视觉表现,它的外观完全由开发者自己在里面写内容——所以要保证这段内容有足够的点击面积,案例通过padding: 20rpx和浅灰背景色把它做得像一个按钮。

9、input type="digit"调出的是带小数点的数字键盘,所以用户可以输入1.5,而1.5 > 0同样成立;如果业务只需要正整数,应该改用type="number",并在colorSet里补一句Number.isInteger(num)校验。键盘类型只是降低输错概率,不能当作数据校验。

10、colorSet绑在bind:blur上,意味着必须点开键盘以外的区域让输入框失焦才会触发,实时性差。如果要边输入边生效,应改成bindinput。失焦还会带来一个副作用:用户输入完直接点别的按钮时,blur 与 tap 的触发顺序在部分机型上并不稳定。

11、Number('')的结果是 0,isNaN(0)为 false,所以清空输入框再失焦,会通过校验并把 digitValue 置为 0,导致 block 整体隐藏。这个行为看起来"正好对",但逻辑上说不通——空输入不应该等价于输入了 0。严谨的写法是先判空:if (e.detail.value.trim() === '') return。另外如果输入内容转不成数字(例如粘贴了文字),isNaN为 true 时直接跳过 setData,页面会保留旧状态,此时输入框里的内容和色块的显示就对不上了。

12、console.log(this.data.color)写在setData之后,能立刻打印出新值——因为this.data是同步更新的,异步的只是视图渲染。这一点常被误解成"setData 之后拿不到新值"。不过案例里两处console.log(e)是调试残留,正式代码应该删掉,避免把用户输入打到控制台。

13、page设为height: 100vh加纵向 flex,.scrollarea反过来占满剩余高度,这是保证 scroll-view 能正确滚动的经典骨架——scroll-view 必须有确定的高度才能滚动,只写flex: 1不写page的高度约束就会失效。不过.scrollarea上那句overflow-y: hidden是多余的,滚动行为已经由 scroll-view 自身接管,父级再写一次容易在低版本上引起滚动异常的排查困难。

七、拓展思考

picker 的 mode 远不止 selector 一种:multiSelector可以做多列联动(比如先选学院再选专业,第二列的数据要随第一列变化,通过bindcolumnchange重新计算 range);date和time可以直接弹出系统日期/时间滚轮,做预约、打卡类表单时不需要自己写日历;region是现成的省市区三级联动,level="city"还能控制到市级还是区级。

条件渲染在真实项目里几乎无处不在:未登录显示登录引导、已登录显示用户信息;列表为空显示空状态插图、有数据显示列表;订单状态决定显示"去支付"还是"申请退款"。这些场景的共同点都是"同一块位置、不同数据、不同内容",而 wx:if 链配合数据里的状态字段(pending / done / closed)就能清晰地表达出来。当分支超过三四条时,更好的做法是把条件判断收敛到 JS 里算出一个状态名,WXML 只做简单的比对,而不是把一长串&&、||塞进表达式里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询