事件处理
事件绑定和事件对象
//pages/home/home.wxml
<button bindtap="handler">按钮</button>
// pages/home/home.js
Page({
// 页面的初始数据
data: {},
// 事件处理程序
handler (event) {
// console.log('我被触发了~~~')
console.log(event)
}
// 其他 coding...
})
绑定并阻止事件冒泡
<view bindtap="parentHandler">
<!-- 使用 bind 绑定的事件,会产生事件冒泡 -->
<!-- <button bindtap="handler">按钮</button> -->
<!-- 使用 catcht 绑定的事件,会阻止事件冒泡 -->
<button catchtap="handler">按钮</button>
</view>
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 设备,重按时会触发 |
事件传参-data-*自定义数据
在 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)
}
})
事件监听-上拉加载更多
小程序中实现上拉加载的方式:
- 在 app.json 或者 page.json 中配置距离页面底部距离: onReachBottomDistance;默认 50px
- 在 页面.js 中定义 onReachBottom 事件监听用户上拉加载
事件监听-下拉刷新
小程序中实现上拉加载更多的方式:
- 在 app.json 或者 page.json 中开启允许下拉,同时可以配置 窗口、loading 样式等
- 在 页面.js 中定义 onPullDownRefresh 事件监听用户下拉刷新
scroll-view加载
scroll-view 上拉加载
bindscrolltolower:滚动到底部/右边时触发 lower-threshold:距底部/右边多远时,触发 scrolltolower 事件 enable-back-to-top:让滚动条返回顶部,iOS 点击顶部状态栏、安卓双击标题栏时,只支持竖向
scroll-view 下拉刷新
知识点:
refresher-enabled:开启自定义下拉刷新
refresher-default-style自定义下拉刷新默认样式支持设置 black | white | none
, none 表示不使用默认样式
refresher-background:自定义下拉刷新区域背景颜色
bindrefresherrefresh:自定义下拉刷新状态回调
refresher-triggered:设置当前下拉刷新状态,(true 下拉刷新被触发,false 表示下拉刷新未被触发,用来关闭下拉效果)
<scroll-view
scroll-y
class="scroll-y"
lower-threshold="100"
bindscrolltolower="getMore"
enable-back-to-top
refresher-enabled
refresher-default-style="black"
refresher-background="#f7f7f8"
bindrefresherrefresh="refreshHandler"
refresher-triggered="{{isTriggered}}"
>
<view wx:for="{{ numList }}" wx:key="*this">{{ item }}</view>
</scroll-view>
渲染问题
列表渲染
<view wx:for="{{ animal }}" wx:for-item="itemName" wx:for-index="i">
{{ itemName.name }} - {{ itemName.avatar }} - {{ i }}
</view>
条件渲染
- 使用
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
})
}
// coding...
}
Page({
// 页面的初始数据
data: {
num: 1
},
// 更新数据
updateNum() {
this.setData({
num: this.data.num + 1
})
}
// coding...
}
原生api(wx.方法)
网络请求
wx.request({
// 接口地址,仅为示例,并非真实的接口地址
url: 'example.php',
// 请求的参数
data: { x: '' },
// 请求方式
method: 'GET|POST|PUT|DELETE',
success (res) {
console.log(res.data)
},
fail(err) {
console.log(err)
}
})
要在小程序管理后台进行设置请求的域名,打开微信公众后台:点击左侧 开发 → 开发管理 → 开发设置 → 服务器域名。域名只支持 https
而且要求已备案
原生弹窗组件
loading加载
loading 提示框常配合网络请求来使用,用于增加用户体验,对应的 API 有两个:
wx.showLoading
显示加载提示框wx.hideLoading
隐藏加载提示框
语法如下:
wx.showLoading({
title: '提示内容', // 提示的内容
mask: true, // 是否显示透明蒙层,防止触摸穿透
success() {}, // 接口调用成功的回调函数
fail() {} // 接口调用失败的回调函数
})
请求结合例子:
Page({
data: {
list: []
},
// 获取数据
getData () {
// 显示 loading 提示框
wx.showLoading({
// 用来显示提示的内容
// 提示的内容不会自动换行,如果提示的内容比较多,因为在同一行展示
// 多出来的内容就会被隐藏
title: '数据加载中...',
// 是否展示透明蒙层,防止触摸穿透
mask: true
})
// 如果需要发起网络请求,需要使用 wx.request API
wx.request({
// 接口地址
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
// 请求方式
method: 'GET',
// 请求参数
data: {},
// 请求头
header: {},
// API 调用成功以后,执行的回调
success: (res) => {
// console.log(res)
if (res.data.code === 200) {
this.setData({
list: res.data.data
})
}
},
// API 调用失败以后,执行的回调
fail: (err) => {
console.log(err)
},
// API 不管调用成功还是失败以后,执行的回调
complete: (res) => {
// console.log(res)
// 关掉 loading 提示框
// hideLoading 和 showLoading 必须结合、配对使用才可以
wx.hideLoading()
}
})
}
})
comfirm选择弹窗
知识点:
wx.showToast()
:消息提示框用来根据用户的某些操作来告知操作的结果,如退出成功给用户提示,提示删除成功等,语法如下:
wx.showToast({
title: '标题', // 提示的内容
duration: 2000, // 提示的延迟时间
mask: true, // 是否显示透明蒙层,防止触摸穿透
icon: 'success', // 图标
success() {}, // 接口调用成功的回调函数
fail() {} // 接口调用失败的回调函数
})
wx.showModal()
模态对话框也是在项目中频繁使用的一个小程序 API
,通常用于向用户询问是否执行一些操作,例如:点击退出登录,显示模态对话框,询问用户是否真的需要退出等等
wx.showModal({
title: '提示', // 提示的标题
content: '您确定执行该操作吗?', // 提示的内容
confirmColor: '#f3514f',
// 接口调用结束的回调函数(调用成功、失败都会执行)
success({ confirm }) {
confirm && consle.log('点击了确定')
}
})
本地存储
同步 API | 异步 API | 作用 |
---|---|---|
wx.setStorageSync | wx.setStorage | 将数据存储在本地缓存中指定的 key 中 |
wx.getStorageSync | wx.getStorage | 从本地缓存中同步获取指定 key 的内容 |
wx.removeStorageSync | wx.removeStorage | 从本地缓存中移除指定 key |
wx.clearStorageSync | wx.clearStorageSync | 清理本地数据缓存 |
路由与通信
在小程序中实现页面的跳转,有两种方式:
- 声明式导航:
navigator
组件 - 编程式导航:使用小程序提供的
API
wx.navigateTo()
:保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面wx.redirectTo()
:关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面wx.switchTab()
:跳转到 tabBar 页面,路径后不能带参数wx.navigateBack()
:关闭当前页面,返回上一页面或多级页面
- 路径后可以带参数,参数需要在跳转到的页面的
**onLoad**
钩子函数中通过形参进行接收
- 参数与路径之间使用
?
分隔 - 参数键与参数值用
=
相连 - 不同参数用
&
分隔 - 例如
path?key=value&key2=value2
自定义组件
创建-注册-使用组件
使用自定义组件 开发中常见的组件主要分为 公共组件 和 页面组件 两种,因此注册组件的方式也分为两种:
- 全局注册:在 app.json 文件中配置 usingComponents 节点进行引用声明,注册后可在任意组件使用
- 局部注册:在页面的 json 文件中配置 usingComponents 节点进行引用声明,只可在当前页面使用
组件中相互传参的例子:
<!--components/custom-checkbox/custom-checkbox.wxml-->
<!-- <text>我是自定义组件</text> -->
<view class="custom-checkbox-container">
<view class="custom-checkbox-box {{ position === 'right' ? 'right' : 'left' }}">
<label class="custom-label">
<checkbox class="custom-checkbox" checked="{{ isChecked }}" bindtap="updateChecked" />
<view class="content">
<!-- lable 和 子节点内容都进行了展示 -->
<!-- 要么展示 lable 要么展示 子节点内容 -->
<!-- 如果用户传递了 lable 属性,就展示 lable -->
<!-- 如果用户没有传递 lable 属性,就展示 子节点内容 -->
<text wx:if="{{ label !== '' }}">{{ label }}</text>
<slot wx:else />
</view>
</label>
</view>
</view>
Component({
options: {
styleIsolation: 'shared'
},
// 如果 styleIsolation 属性值是 shared
// 这时候呢 externalClasses 选项会失效
externalClasses: ['xxxx'],
/**
* 组件的属性列表:组件的对外属性,主要用来接收组件使用者传递给组件内部的属性以及数据
*/
properties: {
// 如果需要接收传递的属性,有两种方式:全写、简写
// label: String
label: {
// type 组件使用者传递的数据类型
// 数据类型:String、Number、Boolean、Object、Array
// 也可以设置为 null,表示不限制类型
type: String,
value: ''
},
position: {
type: String,
value: 'right'
},
// 复选框组件公共组件
// 需要再多个页面、在多个项目中进行使用
// 在使用的时候,有的地方希望默认是选中的效果,有的地方希望默认是没有被选中的效果
// 怎么处理 ?
// 首先让复选框默认还是没有被选中的效果
// 如果希望复选框默认被选中,这时候传递属性(checked=true)到复选框组件
checked: {
type: Boolean,
value: false
}
},
/**
* 组件的初始数据:用来定义当前组件内部所需要使用的数据
*/
data: {
isChecked: false
},
observers: {
// 如果需要将 properties 中的数据赋值给 data
// 可以使用 observers 进行处理
checked: function (newChecked) {
// console.log(newChecked)
this.setData({
isChecked: newChecked
})
}
},
/**
* 组件的方法列表:在组件中,所有的事件处理程序都需要写到 methods 方法中
*/
methods: {
// 更新复选框的状态
updateChecked () {
// this.$emit('ayncCheck', (sss:boolean)=>console.log(sss));
this.setData({
isChecked: !this.data.isChecked,
// label: '在组件内部也可以修改 properties 中的数据'
})
// 在 JS 中可以访问和获取 properties 中的数据
// 但是一般情况下,不建议修改,因为会造成数据流的混乱
// console.log(this.properties.label)
// console.log(this.data.isChecked)
// 目前复选框组件的状态是存储在复选框组件内部的、存储在自定义组件内部的
// 但是,在以后实际开发中,组件使用者、父组件有时候也需要获取到复选框内部的状态
// 怎么办 ?
// 这时候,自定义组件内部就需要发射一个自定义事件,
// 如果组件使用者、父组件需要使用数据,绑定自定义事件进行获取即可
this.triggerEvent('changechecked', this.data.isChecked)
}
}
})
<custom-checkbox label='qweqwe' position='left' bind:changechecked="ayncCheck"/>
ayncCheck:(c:any)=>{
console.log(c.detail);
},
组件 wxml 的 slot
在使用基础组件时,可以给组件传递子节点传递内容,从而将内容展示到页面中,自定义组件也可以接收子节点内容
只不过在组件模板中需要定义 节点,用于承载组件引用时提供的子节点
默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。
同时需要给 slot 添加 name 来区分不同的 slot,给子节点内容添加 slot 属性来将节点插入到 对应的 slot 中
组件 wxml 的 observers(监听数据变化)
Component({
data: {
num: 10,
count: 1,
obj: { name: 'Tom', age: 10 },
arr: [1, 2, 3]
},
observers: {
// key 是需要检测数据
// value 是一个函数,函数接收一个形参作为参数,是最新的值
num: function(newNum) {
console.log(newNum)
},
// 数据监听器支持监听属性或内部数据的变化,可以同时监听多个
'num, count': function (newNum, newCount) {
console.log(newNum, newCount)
}
// 监听器可以监听子数据字段
'obj.age': function(newAge) {
console.log(newAge)
},
// 如果需要监听所有子数据字段的变化,可以使用通配符 **
'obj.**': function(newAge) {
console.log(newAge)
},
'arr[0]': function (val) {}
}
})
传参方式
父往子传值
用properties方式直接传:
<sonTem name="{{fatherName}}" ></sonTem>
<button bindtap="changName">修改fatherName</button>//直接改fatherName,sonTem会绑定并渲染
// components/sonTem/sonTem.ts
Component({
/**
* 组件的属性列表
*/
properties: {
name:{
type: String, // 传递的数据类型
value: '' // 默认值
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
changeName(){
this.setData({name:'lzj'})
}
}
})
或用observers监听传入father的变化:
// components/sonTem/sonTem.ts
Component({
/**
* 组件的属性列表
*/
properties: {
name:{
type: String, // 传递的数据类型
value: '' // 默认值
}
},
/**
* 组件的初始数据
*/
data: {
sonName:"wzz"
},
observers: {
// 如果需要将 properties 中的数据赋值给 data
// 可以使用 observers 进行处理
name: function (newName) {
// console.log(newChecked)
this.setData({
sonName: newName
})
}
},
/**
* 组件的方法列表
*/
methods: {
changeName(){
this.setData({name:'lzj'})
}
}
})
子往父传值
- 自定义组件触发事件时,需要使用
triggerEvent
方法发射一个自定义的事件 - 自定义组件标签上通过 bind 方法监听发射的事件
<sonTem name="{{fatherName}}" bind:nameEvent="syncName"></sonTem>
father:{{fatherName}}
<button bindtap="changName">修改fatherName</button>
--------------------------ts----------------------------
changName:function(){
this.setData({
fatherName:'wgp'
})
},//father修改name的方法
syncName:function(sonName:any){
console.log(sonName);
this.setData({fatherName:sonName.detail})
},//同步name
son:{{name}}
<button bind:tap="changeName">lzj</button>
/**
* 组件的方法列表
*/
methods: {
changeName(){
this.triggerEvent('nameEvent', "son")
}
}
父类获取子类组件
<sonTem name="{{fatherName}}" bind:nameEvent="syncName" class="sonClass"></sonTem>
father:{{fatherName}}
<button type="primary" bindtap="getChild">获取子组件实例对象</button>
/**
* 组件的方法列表
*/
getChild:function () {
// this.selectComponent 方法获取子组件实例对象
// 获取到实例对象以后,就能获取子组件所有的数据、也能调用子组件的方法
const res = this.selectComponent('.sonClass')
console.log(res.data.name)
},
页面组件url传参
onLoad (options) {//回去query内容
// console.log('页面加载 - 2')
// console.log(options)
console.log(this.data.id)
console.log(this.data.title)
console.log(this.properties.id)
},
小程序的生命周期
组件的生命周期
定义段 | 描述 |
---|---|
created | 在组件实例刚刚被创建时执行,注意此时不能调用 setData (还没有对模板解析) |
attached | 在组件实例进入页面节点树时执行 (模板已经解析完毕,并且挂载到页面上) |
ready | 在组件布局完成后执行 |
moved | 在组件实例被移动到节点树另一个位置时执行 |
detached | 在组件实例被从页面节点树移除时执行 (组件被销毁了) |
Component({
lifetimes: {
created: function () {
// 在组件实例刚刚被创建时执行,注意此时不能调用 setData
// 一般用来为组件添加一些自定义属性字段。
},
attached: function() {
// attached 在组件完全初始化完毕、进入页面节点树后执行
// 模板已经解析完毕,并且挂载到页面上
// 一般都是在这里写对应的交互
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
// coding...
}
// coding...
})
组件所在页面的生命周期
组件还有一些特殊的生命周期,这类生命周期和组件没有很强的关联
主要用于组件内部监听父组件的展示、隐藏状态,从而方便组件内部执行一些业务逻辑的处理
组件所在页面的生命周期有 4 个: show、 hide、 resize、 routeDone,需要在 pageLifetimes 字段内进行声明
// components/custom06/custom06.js
Component({
// coding...
// 组件所在页面的生命周期
pageLifetimes: {
// 监听组件所在的页面展示(后台切前台)状态
show () {
console.log('组件所在的页面被展示')
},
// 监听组件所在的页面隐藏(前台切后台、点击 tabBar)状态
hide () {
console.log('组件所在的页面被隐藏')
}
}
})
生命周期总结
小程序冷启动,钩子函数执行的顺序
保留当前页面(navigate) 以及 关闭当前页面(redirect)
切后台 以及 切前台(热启动)