模式切换
数据与事件
数据绑定与渲染
小程序使用 Page()
函数中 data
对象来管理页面的数据,并通过一种特殊的语法将数据与 WXML 绑定在一起,实现数据驱动视图。
数据绑定
使用双大括号 可以将 JavaScript 数据绑定到 WXML 中。
javascript
// index.js
Page({
data: {
message: "Hello World",
count: 0,
isLogin: true,
userInfo: {
name: "张三",
age: 25
},
color: "red"
}
})
html
<!-- index.wxml -->
<view>{{message}}</view>
<view>计数:{{count}}</view>
<view>用户名:{{userInfo.name}}</view>
<view>年龄:{{userInfo.age + 1}}</view> <!-- 支持简单运算 -->
<view style="color: {{color}};">动态样式</view>
<checkbox checked="{{isLogin}}"></checkbox> <!-- 属性绑定也需要 {{}} -->
关键点:
- 内容、组件属性、控制属性、关键字都需要使用
- 支持简单的 JavaScript 表达式(三元运算、算术运算、逻辑判断等)
中可以直接引用
data
中的属性路径
条件渲染 wx:if
根据条件来决定是否渲染某个组件块。
html
<!-- 基本用法 -->
<view wx:if="{{condition}}">条件为真时显示</view>
<view wx:elif="{{anotherCondition}}">另一个条件为真时显示</view>
<view wx:else>以上条件都不满足时显示</view>
<!-- 实际示例 -->
<view wx:if="{{isLogin}}">
<text>欢迎回来,{{userName}}!</text>
<button>退出登录</button>
</view>
<view wx:else>
<button bindtap="onLogin">点击登录</button>
</view>
<!-- 控制多个组件:使用 block -->
<block wx:if="{{isLoading}}">
<view>加载中...</view>
<view>请稍候</view>
</block>
wx:if
vs hidden
:
wx:if
:是惰性的,条件为假时组件不会被渲染,有更高的切换开销hidden
:组件始终会被渲染,只是通过样式控制显示/隐藏,有更高的初始渲染开销
使用建议:
- 需要频繁切换时用
hidden
- 运行时条件不大可能改变时用
wx:if
列表渲染 wx:for
根据数组数据循环渲染组件。
javascript
// index.js
Page({
data: {
students: [
{ id: 1, name: "小明", score: 85 },
{ id: 2, name: "小红", score: 92 },
{ id: 3, name: "小刚", score: 78 }
],
numbers: [10, 20, 30, 40, 50]
}
})
html
<!-- 基本用法 -->
<view wx:for="{{students}}" wx:key="id">
索引:{{index}} - 姓名:{{item.name}} - 分数:{{item.score}}
</view>
<!-- 指定索引和项的变量名 -->
<view wx:for="{{numbers}}" wx:for-index="idx" wx:for-item="num" wx:key="*this">
第{{idx + 1}}个数字是:{{num}}
</view>
<!-- 嵌套循环 -->
<view wx:for="{{classes}}" wx:key="classId">
<text>班级:{{item.className}}</text>
<view wx:for="{{item.students}}" wx:key="id" wx:for-index="studentIndex">
学生{{studentIndex + 1}}:{{item.name}}
</view>
</view>
wx:key
的重要性:
- 用于标识列表项目的唯一性,提高渲染性能
- 可以指定为数组项的某个属性(如
id
) - 如果数组项是简单类型,可以用
*this
(表示数组项本身)
事件系统
事件是视图层到逻辑层的通信方式,用户的操作(点击、输入等)会触发相应的事件。
绑定事件 bindtap、bindinput 等
事件绑定的写法类似 HTML 的属性,以 bind
或 catch
开头,后面跟事件类型。
html
<!-- 点击事件 -->
<button bindtap="onButtonTap">点击我</button>
<!-- 输入事件 -->
<input bindinput="onInputChange" placeholder="请输入内容" />
<!-- 长按事件 -->
<view bindlongpress="onLongPress">长按我</view>
<!-- 触摸事件 -->
<view bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">触摸我</view>
常见事件类型:
tap
:点击longpress
:长按(推荐代替longtap
)input
:输入change
:改变(如 picker, switch)submit
:表单提交scroll
:滚动
bind
vs catch
:
bind
:绑定的事件不会阻止事件冒泡catch
:绑定的事件会阻止事件冒泡
html
<!-- 点击 inner 会触发 onInnerTap 和 onOuterTap -->
<view bindtap="onOuterTap">
<view bindtap="onInnerTap">inner (bind)</view>
</view>
<!-- 点击 inner 只会触发 onInnerTap,不会触发 onOuterTap -->
<view bindtap="onOuterTap">
<view catchtap="onInnerTap">inner (catch)</view>
</view>
事件对象
当事件触发时,逻辑层绑定的处理函数会收到一个事件对象,包含丰富的属性。
javascript
Page({
onButtonTap: function(event) {
// 事件对象包含的信息:
console.log('事件类型:', event.type); // "tap"
console.log('时间戳:', event.timeStamp);
console.log('触发组件:', event.target); // 触发事件的源组件
console.log('当前组件:', event.currentTarget); // 绑定事件的当前组件
console.log('触摸点信息:', event.touches); // 触摸事件特有的触摸点数组
console.log('事件详情:', event.detail); // 额外信息,不同事件有不同的值
},
onInputChange: function(event) {
// 对于 input 事件,detail.value 是输入的值
console.log('输入的值:', event.detail.value);
this.setData({
inputValue: event.detail.value
});
}
})
event.detail
的常见值:
tap
:{x, y}
(点击位置的坐标)input
:{value, cursor}
(输入的值和光标位置)submit
:{value}
(表单数据)change
:根据组件不同而不同(如 switch 返回{value}
)
传递自定义参数 data-*
在组件中可以通过 data-*
属性传递自定义数据,在事件处理函数中通过 event.currentTarget.dataset
获取。
html
<!-- 传递单个参数 -->
<button
bindtap="onItemTap"
data-id="123"
data-name="张三"
>
用户123
</button>
<!-- 在循环中传递当前项的数据 -->
<view wx:for="{{students}}" wx:key="id">
<button
bindtap="onStudentTap"
data-student="{{item}}" <!-- 传递整个对象 -->
data-index="{{index}}"
>
{{item.name}}
</button>
</view>
<!-- 也可以传递到子组件 -->
<navigator
url="/pages/detail/detail"
bindtap="onNavTap"
data-from="homepage"
data-type="product"
>
跳转到详情
</navigator>
javascript
Page({
onItemTap: function(event) {
const dataset = event.currentTarget.dataset;
console.log('ID:', dataset.id); // "123"
console.log('姓名:', dataset.name); // "张三"
// 可以跳转到详情页,携带参数
wx.navigateTo({
url: `/pages/detail/detail?id=${dataset.id}`
});
},
onStudentTap: function(event) {
const dataset = event.currentTarget.dataset;
console.log('索引:', dataset.index);
console.log('学生信息:', dataset.student); // 整个学生对象
// 注意:通过 data- 传递的对象会被转成 JSON 字符串
// 接收时又会被转回对象,但会失去方法
},
onNavTap: function(event) {
console.log('来源:', event.currentTarget.dataset.from); // "homepage"
console.log('类型:', event.currentTarget.dataset.type); // "product"
}
})
data-*
的命名规则:
- 在 WXML 中:
data-xxx-yyy
- 在 JS 中:通过
dataset.xxxYyy
访问(驼峰命名) - 例如:
data-user-id
→dataset.userId
示例:
html
<!-- 一个简单的计数器 -->
<view>
<text>当前计数:{{count}}</text>
<button bindtap="onIncrement" data-amount="1">+1</button>
<button bindtap="onIncrement" data-amount="5">+5</button>
<button bindtap="onReset">重置</button>
</view>
javascript
Page({
data: {
count: 0
},
onIncrement: function(event) {
const amount = parseInt(event.currentTarget.dataset.amount);
this.setData({
count: this.data.count + amount
});
},
onReset: function() {
this.setData({
count: 0
});
}
})