第三章 WXML 表单组件全览与实战
第三章 WXML 表单组件全览与实战
📚 系列教程:微信小程序投票系统完整开发
🔗 上一章:第二章 - 小程序目录结构与核心文件详解
🔗 下一章:第四章 - WXSS 样式系统与布局
3.1 WXML 是什么
WXML(WeiXin Markup Language)是小程序的模板语言,类似 HTML,但有以下区别:
| 对比 | HTML | WXML |
|---|---|---|
| 基础容器 | <div> | <view> |
| 文字 | <span> | <text> |
| 图片 | <img> | <image> |
| 链接 | <a> | <navigator> |
| 数据绑定 | 无原生支持 | {{变量名}} |
| 列表渲染 | 无原生支持 | wx:for |
| 条件渲染 | 无原生支持 | wx:if |
3.2 数据绑定语法
<!-- .wxml 文件 --><view>{{title}}</view><view>{{count + 1}}</view><view>{{isShow ? '显示' : '隐藏'}}</view><imagesrc="{{imgUrl}}"/><inputvalue="{{inputVal}}"/>// .js 文件Page({data:{title:'投票系统',count:10,isShow:true,imgUrl:'/images/logo.png',inputVal:'默认值'}})3.3 基础容器组件
view — 块级容器(最常用)
<!-- 基础用法 --><viewclass="container">内容</view><!-- hover 点击效果 --><viewhover-class="hover"hover-stay-time="100">点我有变色</view>text — 文字组件
<!-- 基础文字 --><text>普通文字</text><!-- 可选中文字(用户可长按复制) --><textselectable="true">可选中的文字</text><!-- 解析转义字符 --><textdecode="true">5>3</text><!-- 多行文字与换行 --><text>第一行\n第二行</text>📌
<text>内部不能嵌套<view>,只能放文字和<text>。
image — 图片组件
<!-- 基础图片 --><imagesrc="/images/logo.png"mode="aspectFit"/><!-- 网络图片 --><imagesrc="https://example.com/img.jpg"mode="aspectFill"/><!-- 懒加载(进入视口才加载) --><imagesrc="{{item.imgUrl}}"lazy-load="true"/>mode 属性(重要)
| mode | 说明 |
|---|---|
scaleToFill | 拉伸填满,可能变形(默认) |
aspectFit | 保持比例,完整显示,可能留白 |
aspectFill | 保持比例,填满容器,可能裁剪 ⭐ |
widthFix | 宽度固定,高度自适应 |
scroll-view — 可滚动视图
<!-- 纵向滚动 --><scroll-viewscroll-y="true"style="height:500rpx;"bindscrolltolower="onLoadMore"><viewwx:for="{{list}}"wx:key="id">{{item.name}}</view></scroll-view><!-- 横向滚动 --><scroll-viewscroll-x="true"style="white-space:nowrap;"><viewstyle="display:inline-block;"wx:for="{{tabs}}"wx:key="id">{{item}}</view></scroll-view>3.4 表单组件详解
input — 输入框
<inputtype="text"value="{{form.title}}"placeholder="请输入投票标题"placeholder-class="placeholder-style"placeholder-style="color: #ccc; font-size: 28rpx;"maxlength="50"disabled="{{false}}"focus="{{autoFocus}}"confirm-type="done"bindinput="onTitleInput"bindfocus="onFocus"bindblur="onBlur"bindconfirm="onConfirm"/>type 属性值
| type | 说明 | 键盘样式 |
|---|---|---|
text | 普通文本(默认) | 普通键盘 |
number | 数字 | 数字键盘 |
digit | 带小数的数字 | 数字+小数点键盘 |
idcard | 身份证 | 数字+X键盘 |
nickname | 昵称(会弹出填写昵称弹窗) | 特殊处理 |
事件说明
onTitleInput(e){// e.detail.value = 当前输入框的完整值(不是变化量!)this.setData({'form.title':e.detail.value})},onFocus(e){// e.detail.height = 键盘高度(可用于调整布局)},onBlur(e){// 失去焦点时触发},onConfirm(e){// 点击键盘右下角确认键时触发// e.detail.value = 当前值}textarea — 多行输入框
<textareavalue="{{content}}"placeholder="请输入详细描述(选填)"maxlength="200"auto-height="true"show-confirm-bar="false"bindinput="onContentInput"style="width:100%;"/>⚠️注意:textarea 在 iOS 上有覆盖层问题,如果页面有 fixed 定位的元素(比如底部按钮),需要特殊处理。
button — 按钮
<!-- 三种类型 --><buttontype="primary">绿色主按钮</button><buttontype="default">白色默认按钮</button><buttontype="warn">红色警告按钮</button><!-- 小尺寸 --><buttonsize="mini"type="primary">小按钮</button><!-- 镂空样式 --><buttonplain="true"type="primary">镂空按钮</button><!-- loading 状态 --><buttonloading="{{isSubmitting}}"disabled="{{isSubmitting}}"type="primary">{{isSubmitting ? '提交中...' : '立即提交'}}</button><!-- 开放能力 --><buttonopen-type="share">分享给朋友</button><buttonopen-type="getUserInfo"bindgetuserinfo="onGetUserInfo">获取头像昵称</button><buttonopen-type="getPhoneNumber"bindgetphonenumber="onGetPhone">获取手机号(需企业认证)</button>radio — 单选框(投票单选必用)
<!-- 方式一:radio-group 包裹(推荐) --><radio-groupbindchange="onTypeChange"><labelclass="radio-item"><radiovalue="1"checked="{{form.type === '1'}}"/><text>单选投票</text></label><labelclass="radio-item"><radiovalue="2"checked="{{form.type === '2'}}"/><text>多选投票</text></label></radio-group>onTypeChange(e){// e.detail.value = 被选中 radio 的 value 值this.setData({'form.type':e.detail.value})}checkbox — 复选框(投票多选必用)
<checkbox-groupbindchange="onOptionsChange"><viewwx:for="{{options}}"wx:key="id"class="option-item"><checkboxvalue="{{item.id}}"checked="{{item.checked}}"/><text>{{item.content}}</text></view></checkbox-group>onOptionsChange(e){// e.detail.value = 所有选中项的 value 组成的数组// 例:['1', '3', '5']this.setData({selectedOptionIds:e.detail.value})}⚠️注意:微信原生 checkbox 的样式很丑,投票系统中我们用自定义样式来实现更好看的效果(第九章详解)。
switch — 开关
<switchchecked="{{isMultiple}}"bindchange="onSwitchChange"color="#07C160"type="switch"/>onSwitchChange(e){// e.detail.value = true/falsethis.setData({isMultiple:e.detail.value})}picker — 选择器
<!-- 普通选择器 --><pickermode="selector"range="{{typeArray}}"range-key="label"value="{{typeIndex}}"bindchange="onPickerChange"><viewclass="picker-display">当前选择:{{typeArray[typeIndex].label}}</view></picker><!-- 日期选择器 --><pickermode="date"value="{{endDate}}"start="2024-01-01"bindchange="onDateChange"><view>截止日期:{{endDate}}</view></picker><!-- 多列选择器 --><pickermode="multiSelector"value="{{multiVal}}"range="{{multiArray}}"bindchange="onMultiChange"><view>{{multiVal}}</view></picker>slider — 滑块
<slidermin="1"max="10"step="1"value="{{maxChoices}}"show-value="true"active-color="#07C160"bindchange="onSliderChange"bindchanging="onSliderChanging"/>form — 表单整体提交
<formbindsubmit="onFormSubmit"bindreset="onFormReset"><inputname="title"value="{{form.title}}"placeholder="投票标题"/><radio-groupname="type"><radiovalue="1"checked/>单选<radiovalue="2"/>多选</radio-group><buttonform-type="submit">提交</button><buttonform-type="reset">重置</button></form>onFormSubmit(e){// e.detail.value = { title: '...', type: '1' }// 所有有 name 属性的表单元素的值会被收集到这里console.log(e.detail.value)}3.5 列表渲染 wx:for
<!-- 基础用法 --><viewwx:for="{{options}}"wx:key="id">{{item.content}}</view><!-- 自定义变量名(防止嵌套时冲突)--><viewwx:for="{{voteList}}"wx:key="id"wx:for-item="vote"wx:for-index="idx"><viewwx:for="{{vote.options}}"wx:key="id"wx:for-item="opt">{{idx}}. {{vote.title}} - {{opt.content}}</view></view>wx:key 的值
wx:key="id" ← 使用数组元素的某个属性(推荐,必须唯一) wx:key="*this" ← 使用数组元素本身(适合字符串数组) wx:key="index" ← 用下标(不推荐,会有性能警告)3.6 条件渲染 wx:if
<!-- wx:if:条件为 false 时销毁 DOM(适合切换不频繁的场景) --><viewwx:if="{{type === 'single'}}">单选模式</view><viewwx:elif="{{type === 'multi'}}">多选模式</view><viewwx:else>未设置</view><!-- hidden:条件为 true 时隐藏(display:none,DOM 保留,适合频繁切换)--><viewhidden="{{!isExpanded}}">展开的内容</view>📌选择依据:
- 初次渲染较重、切换不频繁 → 用
wx:if- 需要频繁切换(如 Tab 内容)→ 用
hidden
3.7 模板 template
将重复使用的 WXML 片段抽取为模板:
<!-- 定义模板 --><templatename="voteCard"><viewclass="vote-card"><textclass="title">{{title}}</text><textclass="count">{{count}}人参与</text></view></template><!-- 使用模板 --><templateis="voteCard"data="{{title: item.title, count: item.totalVotes}}"/>3.8 事件系统详解
事件绑定方式
<!-- bind:冒泡事件(会向父元素传播) --><viewbindtap="onTap">点击冒泡</view><!-- catch:阻止冒泡 --><viewcatchtap="onTap">点击不冒泡</view><!-- mut-bind:互斥事件(同层中只有一个能触发) --><viewmut-bind:tap="onTap">互斥点击</view>传递参数><viewbindtap="onVoteItemClick"data-id="{{item.id}}"data-title="{{item.title}}"data-index="{{index}}">{{item.title}}</view>onVoteItemClick(e){// dataset 中的键名会自动转换为小驼峰const{id,title,index}=e.currentTarget.dataset// e.currentTarget.dataset ← 绑定事件的元素的 dataset// e.target.dataset ← 触发事件的元素的 dataset(可能是子元素)}
常用事件类型
onVoteItemClick(e){// dataset 中的键名会自动转换为小驼峰const{id,title,index}=e.currentTarget.dataset// e.currentTarget.dataset ← 绑定事件的元素的 dataset// e.target.dataset ← 触发事件的元素的 dataset(可能是子元素)}| 事件 | 触发时机 |
|---|---|
tap | 手指触摸后抬起(类似 click) |
longpress | 长按 300ms+ |
touchstart | 手指触摸开始 |
touchmove | 手指移动 |
touchend | 手指触摸结束 |
input | 输入框内容变化 |
focus | 获得焦点 |
blur | 失去焦点 |
change | radio/checkbox/picker 等值变化 |
submit | 表单提交 |
scroll | 滚动时 |
本章小结
✅ 掌握了 WXML 与 HTML 的核心区别
✅ 学会了所有表单组件的使用(input/textarea/button/radio/checkbox/picker 等)
✅ 掌握了wx:for列表渲染和wx:key的正确使用
✅ 理解了wx:if和hidden的使用场景区别
✅ 学会了通过data-xxx在事件中传递参数
下一章:学习 WXSS 样式系统,包括 rpx 单位、Flex 布局,让投票界面变得好看。
章节:3 / 15 | 更新时间:2026-05-18
