微信小程序_2
四、事件处理
一个应用仅仅只有界面展示是不够的,还需要和用户做交互,
例如:响应用户的点击、获取用户输入的值等等,在小程序里边,
我们就通过编写
JS脚本文件来处理用户的操作
4.1 事件绑定&事件对象
小程序中绑定事件与在网页开发中绑定事件几乎一致,只不过在小程序不能通过
on的方式绑定事件,也没有
click等事件,小程序中绑定事件使用bind方法,click事件也需要使用tap事件来进行代替,绑定事件的方式有两种:
- 第一种方式:
bind:事件名,bind后面需要跟上冒号,冒号后面跟上事件名
<button bind:tap="handler">按钮</button>- 第二种方式:
bind事件名,bind后面直接跟上事件名
<button bindtap="handler">按钮</button>事件处理函数需要写到
.js文件中,在.js文件中需要调用小程序提供的Page方法来注册小程序的页面,我们可以直接在
Page方法中创建事件处理函数。例如:
// pages/home/home.js
Page({
// 页面的初始数据
data: {},
// 事件处理程序
handler () {
console.log('我被执行啦~~~')
}
// 其它 code....
})当组件触发事件时,绑定的事件的处理函数会收到一个事件对象,用来记录事件发生时的相关信息。
在触发事件时,事件处理程序会主动的给我们传入一个参数 ——
event(事件对象)
// pages/home/home.js
Page({
// 页面的初始数据
data: {},
// 事件处理程序
handler (event) {
// console.log('我被触发了~~~')
console.log(event)
}
// 其他 code...
})4.2 绑定&组织事件冒泡
事件分为冒泡事件和非冒泡事件:
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
- 使用
bind绑定的事件,会触发事件冒泡,如果想阻止事件冒泡,可以使用catch来绑定事件。
<view bindtap="parentHandler">
<!-- 使用 bind 绑定的事件,会产生事件冒泡 -->
<!-- <button bindtap="handler">按钮</button> -->
<!-- 使用 catcht 绑定的事件,会阻止事件冒泡 -->
<button catchtap="handler">按钮</button>
</view>Page({
// 页面的初始数据
data: {},
// 事件处理程序
handler (event) {
console.log('我是子绑定的事件 ~~~')
},
parentHandler () {
console.log('我是父绑定的事件 ~~~')
}
// 其他 code...
})WXML 中冒泡事件列表如下表:
| 类型 | 触发条件 |
|---|---|
touchstart | 手指触摸动作开始 |
touchmove | 手指触摸后移动 |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 |
touchend | 手指触摸动作结束 |
tap | 手指触摸后马上离开(点击) |
longpress | 手指触摸后,超过 350ms 再离开(长按), 如果指定了事件回调函数并触发了这个事件, tap 事件将不被触发 |
longtap | 手指触摸后,超过 350ms 再离开(推荐使用 longpress 事件代替) |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 |
animationstart | 会在一个 WXSS animation 动画开始时触发 |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 |
animationend | 会在一个 WXSS animation 动画完成时触发 |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
4.3 事件传参
4.3.1 data-* 自定义数据
在小程序中,可以通过事件传参的方式,将数据传递给事件处理函数。
常见的事件包括点击事件、输入事件等。
在组件节点中可以通过
data-的方式传递一些自定义数据,传递的数据可以通过事件对象的方式进行获取
📌 注意事项:
使用
data-方法传递参数的时候,多个单词由连字符-连接连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符
例如:
data-element-type,最终会呈现为event.currentTarget.dataset.elementTypedata-elementType,最终会呈现为event.currentTarget.dataset.elementtype
- 在
wxml文件中,使用data-*属性将数据传递给事件处理函数。例如:
<view bindtap="parentHandler" data-parent-id="1" data-parentName="tom">
<!-- 如果需要进行事件传参,需要再组件上通过 data- 的方式传递数据 -->
<!-- <button bindtap="btnHandler" data-id="1" data-name="tom">按钮</button> -->
<button data-id="1" data-name="tom">按钮</button>
</view>- 在
js文件中,可以通过event.currentTarget.dataset获取传递的数据
// cate.js
Page({
// 按钮触发的事件处理函数
btnHandler (event) {
// currentTarget 事件绑定者,也就是指:哪个组件绑定了当前事件处理函数
// target 事件触发者,也就是指:哪个组件触发了当前事件处理函数
// currentTarget 和 target 都是指按钮,因为是按钮绑定的事件处理函数,同时点击按钮触发事件处理函数
// 这时候通过谁来获取数据都可以
console.log(event.currentTarget.dataset.id)
console.log(event.target.dataset.name)
},
// view 绑定的事件处理函数
parentHandler (event) {
// 点击蓝色区域(不点击按钮)
// currentTarget 事件绑定者:view
// target 事件触发者:view
// currentTarget 和 target 都是指 view,如果想获取 view 身上的数据,使用谁都可以
// 点击按钮(不点击蓝色区域)
// currentTarget 事件绑定者:view
// target 事件触发者:按钮
// 如果想获取 view 身上的数据,就必须使用 currentTarget 才可以
// 如果想获取的是事件触发者本身的数据,就需要使用 target
console.log(event)
// 在传递参数的时候,如果自定义属性是多个单词,单词与单词直接使用中划线 - 进行连接
// 在事件对象中会被转换为小托峰写法
console.log(event.currentTarget.dataset.parentId)
// 在传递参数的时候,如果自定义属性是多个单词,单词如果使用的是小托峰写法
// 在事件对象中会被转为全部小写的
console.log(event.currentTarget.dataset.parentname)
}
})4.3.2 mark 自定义数据
小程序进行事件传参的时候,除了使用
data-*属性传递参数外,还可以使用mark标记传递参数mark是一种自定义属性,可以在组件上添加,用于来识别具体触发事件的target节点。同时
mark还可以用于承载一些自定义数据(类似于dataset)
mark和dataset很相似,主要区别在于:mark会包含从触发事件的节点到根节点上所有的mark:属性值 (事件委托的)dataset仅包含触发事件那一个节点的data-属性值。
- 在
wxml文件中,使用mark:自定义属性的方式将数据传递给事件处理函数
<!-- pages/index/index.wxml -->
<view bindtap="parentHandler" mark:parentid="1" mark:parentname="tom">
<!-- 如果需要使用 mark 进行事件传参,需要使用 mark:自定义属性的方式进行参数传递 -->
<!-- <button bindtap="btnHandler" mark:id="1" mark:name="tom">按钮</button> -->
<button mark:id="1" mark:name="tom">按钮</button>
</view>// cart.js
Page({
// 按钮绑定的事件处理函数
btnHandler (event) {
console.log(event.mark.id)
console.log(event.mark.name)
},
// view 绑定的事件处理函数
parentHandler (event) {
// 先点击蓝色区域 (不点击按钮)
// 通过事件对象获取的是 view 身上绑定的数据
// 先点击按钮 (不点击蓝色区域)
// 通过事件对象获取到的是 触发事件的节点 以及 父节点身上所有的 mark 数据
console.log(event)
}
})五、模版语法
5.1 声明&绑定数据
小程序页面中使用的数据均需要在
Page()方法的data对象中进行声明定义在将数据声明好以后,需要在
WXML中绑定数据,数据绑定最简单的方式是使用
Mustache语法(双大括号)(其实就是VUE的插值表达式)将变量包起来。
在 {{ }} 内部可以做一些简单的运算,支持如下几种方式:
算数运算
三元运算
逻辑判断
其它...
📌 注意事项:
在 {{ }} 语法中,只能写表达式,不能写语句,也不能调用 js 相关的方法
- 定义数据:
Page({
// 页面的初始数据
data: {
num: 1
}
// code...
}- 使用数据:
<!-- 如果需要展示数据,在 wxml 中需要使用双大括号写法将变量进行包裹 -->
<!-- 展示内容 -->
<view>{{ school }}</view>
<view>{{ obj.name }}</view>
<!-- 绑定属性值,如果需要动态绑定一个变量,属性值也需要使用双大括号进行包裹 -->
<view id="{{ id }}">绑定属性值</view>
<!-- 如果属性值是布尔值,也需要使用双大括号进行包裹 -->
<checkbox checked="{{ isChecked }}" />
<!-- 算术运算 -->
<view>{{ id + 1 }}</view>
<view>{{ id - 1 }}</view>
<!-- 三元运算 -->
<view>{{ id === 1 ? '等于' : '不等于' }}</view>
<!-- 逻辑判断 -->
<view>{{ id === 1 }}</view>
<!-- 在双大括号写法内部,只能写表达式,不能写语句,也不能调用 js 的方法 -->
<!-- <view>{{ if (id === 1) {} }}</view> -->
<!-- <view>{{ for (const i = 0; i <= 10; i++) {} }}</view> -->
<!-- <view>{{ obj.name.toUpperCase() }}</view> -->- 例如:
// index.js
Page({
// 在小程序页面中所需要使用的数据均来自于 data 对象
data: {
id: 1,
isChecked: false,
school: 'iraionly',
obj: {
name: 'tom'
}
}
})5.2 声明&修改数据
小程序中修改数据并不能直接进行赋值,而是要通过调用
this.setData方法才能实现将需要修改的数据以
key:value的形式传给this.setData方法。
this.setData方法有两个作用:更新数据
驱动视图更新
Page({
// 页面的初始数据
data: {
num: 1
},
updateNum() {
this.setData({
// key 是需要修改的数据
// value 是最新值
num: this.data.num + 1
})
}
// code...
}5.3 setData 详解
5.3.1 修改对象类型数据
在实际开发中,我们经常会在
data中声明对象类型的数据,小程序中通过调用
setData方法可以修改页面的数据,包括对象类型的数据。下面是修改对象类型数据的方法:
- 定义一个对象
Page({
// 定义页面中使用的数据
data: {
userInfo: {
name: 'Tom',
age: 10,
gender: '男'
}
}
}- 修改对象中的单个属性
this.setData({
'userInfo.name': 'Jerry'
})- 修改对象中的多个属性
// 修改对象中的多个属性
this.setData({
'userInfo.name': 'Jerry',
'userInfo.age': 100
})- 使用
ES6的展开运算符
在修改对象类型的数据时,可以使用
ES6的展开运算符先复制对象,然后利用新值对旧值覆盖的方式修改
const userInfo = {
...this.data.userInfo,
name: 'Jerry',
age: 100
}
// 修改对象中的多个属性
this.setData({
// userInfo:userInfo
// 键和值一样可以简写
userInfo
})- 使用
Object.assign方法合并对象
在修改对象类型的数据时,可以使用
Object.assign方法将多个对象合并为一个对象
// 使用 Object.assign 方法将多个对象合并(从后往前合并)为一个对象
const userInfo = Object.assign(
this.data.userInfo,
{ name: 'Jerry' },
{ age: 100 }
)
// 修改对象中的多个属性
this.setData({
// userInfo:userInfo
// 键和值一样可以简写
userInfo
})- 删除对象中的属性
在删除对象中的属性时,不能使用
delete操作符,因为小程序的数据绑定机制不支持监听delete操作
// 使用展开运算符拷贝一份数据,产生一个新对象
const newUser = { ...this.data.userInfo }
// 使用 delete 删除新对象中的属性
delete newUser.age
this.setData({
// 将新的对象进行赋值
userInfo: newUser
})📌 注意事项:
小程序的数据绑定机制只能监听到
setData方法中修改的数据,无法监听到直接删除属性的操作,所以在删除对象属性时,需要先将对象复制一份再进行操作,然后再调用
setData方法更新数据。
5.3.2 修改数组类型数据
- 数组类型数据也是经常会使用的数据格式之一,下面是修改数组类型数据的方法:
- 定义一个数组
Page({
// 定义页面中使用的数据
data: {
animalList: ['Tom', 'Jerry', 'irai']
}
// code...
}- 使用数组的
concat方法合并数组
在修改数组类型的数据时,可以使用数组的
concat方法来合并数组
// 使用 concat 方法来合并数组
const newList = this.data.animalList.concat('Tyke')
// 使用 setData 进行赋值
this.setData({
animalList: newList
})- 使用数组的
push方法新增属性
在修改数组类型的数据时,可以使用数组的
push方法来添加元素
// 使用数组的 push 方法来添加元素
this.data.animalList.push('Tyke')
// 使用 setData 进行赋值
this.setData({
animalList: this.data.animalList
})- 使用
ES6的展开运算符
在数组类型的数据时,可以使用 ES6 的展开运算符先复制数组,然后进行合并
// 使用 ES6 的展开运算符先复制数组,然后进行合并
const newList = [...this.data.animalList, 'Tyke']
// 使用 setData 进行赋值
this.setData({
animalList: newList
})- 修改数组中某个元素的值:
利用索引的方式进行修改,但必须使用
wx:for来进行渲染数组,否则会出现错误
this.setData({
'animalList[2]': 'Tyke'
})- 使用数组的
filter方法删除元素
// 使用数组的 filter 方法来删除元素
// 在下面例子 newList 过滤出来的是数组中不等于 Tom 的元素
const newList = this.data.animalList.filter(item => item !== 'Tom')
// 使用 setData 进行赋值
this.setData({
animalList: newList
})5.4 简易双向绑定
- 在
WXML中,普通属性的绑定是单向的,例如:
<input value="{{ num }}" />如果使用
this.setData来更新num,num和输入框的中显示的值都会被更新为值。但如果用户修改了输入框里的值,却不会同时改变
data中的num。也就是说:单向数据绑定指的是 :
数据修改可以驱动视图,视图变化不能改变数据
如果需要在用户输入的同时也将
data中的数据修改 ,需要借助简易双向绑定机制。此时可以在对应项目之前加入
model:前缀即可,例如:
<input model:value="{{ value }}" />如果使用
this.setData来更新num,num和输入框的中显示的值都会被更新为值。如果手动修改了输入框的值,
data的数据也会随着改变。同时,
WXML中所有绑定了数据的位置也会被一同更新
📌简易双向绑定的属性值如下限制:
只能是一个单一字段的绑定,例如:错误用法:
<input model:value="值为 {{value}}" />尚不能写
data路径,也就是不支持数组和对象,例如:错误用法:
<input model:value="<span v-pre>{{a.b}}</span>" />
5.5 列表渲染
5.5.1 基本使用
列表渲染:就是指通过循环遍历一个数组或对象,将其中的每个元素渲染到页面上
只需要在组件上使用
wx:for属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件
默认数组当前项的变量名默认为
item默认数组的当前项的下标变量名默认为
index
在使用
wx:for对数组进行遍历的时候,建议加上wx:key属性,如不提供wx:key,会报一个
warning, 如果明确知道该列表是静态,即以后数据不会改变,或者不必关注其顺序,可以选择忽略。
wx:key的值以两种形式提供:字符串:代表需要遍历的
array中item的某个property,该
property的值需要是列表中唯一的字符串或数字,且不能动态改变保留关键字
*this代表在for循环中的item本身,当
item本身是一个唯一的字符串或者数字时可以使用
当数据改变触发渲染层重新渲染的时候,会校正带有
key的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且 提高列表渲染时的效率 。
📌 注意事项:
在使用
wx:for对数组进行遍历的时候,建议加上wx:key属性,否则控制台会报警告
- 示例代码:
<!-- 如果需要进行列表渲染,需要使用 wx:for 属性 -->
<!-- 属性值需要使用双大括号进行包裹 -->
<!-- 每一项的变量名默认是 item -->
<!-- 每一项下标(索引)的变量名默认是 index -->
<!-- 如果渲染的是数组,item:数组的每一项,index:下标 -->
<!-- <view wx:for="{{ numList }}">{{ item }} - {{ index }}</view> -->
<!-- 如果渲染的是对象,item:对象属性的值,index:对象属性 -->
<!-- <view wx:for="{{ obj }}">{{ item }} - {{ index }}</view> -->
<!-- ------------------------ 关于 Key --------------------------------- -->
<!-- wx:key 提升性能 -->
<!-- wx:key 的属性值不需要使用双大括号进行包裹,直接写遍历的数组 中 item 的某个属性 -->
<!-- wx:key 属性值有两种添加形式 -->
<!-- 字符串,需要是遍历的数组 中 item 的某个属性,要求该属性是列表中唯一的字符串或者数字,不能进行动态改变 -->
<view wx:for="{{ fruitList }}" wx:key="id">{{ item.name }}</view>
<view wx:for="{{ fruitList }}" wx:key="index">{{ item.name }}</view>
<!-- 保留关键字 *this,*this 代表的是 item 本身,item 本身是唯一的字符串或者数字 -->
<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>// profile.js
Page({
data: {
numList: [1, 2, 3],
fruitList: [
{ id: 1, name: '🍎', price: 66 },
{ id: 2, name: '🍋', price: 77 },
{ id: 3, name: '🍅', price: 88 }
],
obj: {
name: 'tom',
age: 10
}
}
})5.5.2 使用进阶
修改默认下标和变量名:可以使用
wx:for-item和wx:for-index使用
wx:for-item可以指定数组当前元素的变量名使用
wx:for-index可以指定数组当前下标的变量名
- 示例:
<view wx:for="{{ animal }}" wx:for-item="itemName" wx:for-index="i">
{{ itemName.name }} - {{ itemName.avatar }} - {{ i }}
</view>- 渲染多节点结构块:
如果需要渲染一个包含多节点的结构块,可以使用一个
<block/>标签将多个组件包装起来
<block wx:for="{{ animal }}">
<view>
<span>{{ item.name }}</span>
<span>{{ item.avatar }}</span>
</view>
</block>- 注意:
<block/>并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
5.6 条件渲染
- 条件渲染主要用来控制页面结构的展示和隐藏,在微信小程序中实现条件渲染有两种方式:
- 使用
wx:if、wx:elif、wx:else属性组 - 使用
hidden属性
- 使用
- 示例结构:
<view wx:if="{{condition}}"> True </view><view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view<view hidden="{{condition}}"> True </view>📌 wx:if 和 hidden 二者的区别:
wx:if:当条件为true时将内容渲染出来,否则元素不会进行渲染,通过移除/新增节点的方式来实现hidden:当条件为true时会将内容隐藏,否则元素会显示内容,通过display样式属性来实现的
- 示例代码:
<!-- 使用 wx:if、wx:elif、wx:else 属性组控制元素的隐藏和控制 -->
<view wx:if="{{ num === 1 }}">num 等于 {{ num }}</view>
<view wx:elif="{{ num === 2 }}">num 等于 {{ num }}</view>
<view wx:else>大于 2</view>
<view hidden="{{ num !== 1 && num !== 2 && num !== 3 && num < 3}}">
{{ num < 3 ? 'num 等于' + num : '大于 2' }}
</view>
<button type="primary" bindtap="updateNum">修改数据</button>Page({
// 页面的初始数据
data: {
num: 1
},
// 更新数据
updateNum() {
this.setData({
num: this.data.num + 1
})
}
// code...
}