第3章
小程序开发基础
本章将从架构层面介绍小程序的开发框架,包括视图层描述语言WXML(WeiXin Markup Language,微信标记语言)和WXSS(WeiXin Style Sheets,微信样式表),以及基于JavaScript 的逻辑层等。
本章学习目标:
了解小程序的生命周期与页面的生命周期。
了解小程序框架的基本功能。
了解接口和组件。
熟悉小程序注册和页面注册的方法。
熟悉模板,样式导入,模块化的操作方法。
掌握数据绑定的用法。
掌握列表渲染和条件渲染的使用方法。
掌握事件的绑定与处理方法。
掌握小程序样式文件的使用方法。
3.1认识小程序的生命周期
【任务要求】
修改示例项目中的app.js文件,要求当小程序在执行以下操作时在调试器的Console面板中输出对应的信息。
(1) 当小程序第一次启动时,输出当前系统的时间。
(2) 当小程序被放置到后台时,输出“小程序已被隐藏”。
(3) 当小程序从后台被唤醒时,输出进入的场景值。
【任务分析】
本次任务主要涉及对小程序生命周期中三种状态的处理,一个是第一次启动,然后是隐藏和显示。其中比较重要的一点是当小程序从后台被唤醒时,携带的包含场景值的参数,它可以用来帮助开发者更好地响应用户的操作。
【任务操作】
(1) 打开示例项目的app.js文件,在onLaunch()函数内部的开始处,新增如下所示的两行代码。
//引用util文件夹中的公共函数
const util = require('utils/util.js')
//将当前系统时间格式化输出
console.log(util.formatTime(new Date(Date.now())))
(2) 在app.js文件的globalData参数前,新增如下用于处理小程序被隐藏和唤醒事件的函数。
onShow:function(opts) {
//输出opts中表示场景值的scene变量
console.log(opts.scene)
},
onHide:function() {
console.log('小程序已被隐藏')
},
完成后的app.js文件内容结构大致如下。
App({
onLaunch:function(opts) {
const util = require('utils/util.js')
console.log(util.formatTime(new Date(Date.now())))
// 展示本地存储能力
…
},
onShow:function(opts) {
console.log(opts.scene)
},
onHide:function() {
console.log('小程序已被隐藏')
},
globalData: {
userInfo: null
}
})
微信小程序实用教程
第
3
章小程序开发基础
注意: 代码中加粗部分内容仅作阅读重点提示用,无其他特殊含义。下同。
(3) 保存文件,编译项目,在Console面板中查看小程序启动时输出的时间信息,如图31所示。
图31小程序启动时的输出
可以看到,小程序的第一次启动触发了onLaunch()函数和onShow()函数。
(4) 在工具栏中单击“切后台”按钮,让小程序进入后台状态,然后在模拟器中选择“1011: 扫描二维码”,唤醒小程序到前台。在这个过程中观察到的输出如图32所示。
图32小程序转入后台和切换前台时输出
【相关知识】
小程序主要有前台和后台两种运行状态。当小程序处于前台时,它可以调用所有的API(Application Programming Interface,应用程序编程接口),为用户提供服务; 当用户单击小程序运行页面左上角“关闭”按钮,或者按了设备Home键离开微信,小程序就转入了后台,此时小程序只能调用部分API,并随时可能被销毁。小程序只有在进入后台一定时间后,或者系统资源占用过高时,才会被真正销毁。当用户再次进入微信或再次打开小程序,又会从后台变为前台状态。
场景值表示的是小程序是通过什么途径从后台切换到前台的。目前小程序支持的场景值说明见表31。
表31小程序场景值说明
场景值ID
说明
场景值ID
说明
场景值ID
说明
1001
发现栏小程序主入口,“最近使用”列表(基础库2.2.4版本起包含“我的小程序”列表)
1032
手机相册选取一维码
1064
微信连WiFi状态栏
1005
顶部搜索框的搜索结果页
1034
微信支付完成页
1067
公众号文章广告
1006
发现栏小程序主入口搜索框的搜索结果页
1035
公众号自定义菜单
1068
附近小程序列表广告
1007
单人聊天会话中的小程序消息卡片
1036
App分享消息卡片
1069
移动应用
1008
群聊会话中的小程序消息卡片
1037
小程序打开小程序
1071
钱包中的银行卡列表页
1011
扫描二维码
1038
从另一个小程序返回
1072
二维码收款页面
1012
长按图片识别二维码
1039
摇电视
1073
客服消息列表下发的小程序消息卡片
1013
手机相册选取二维码
1042
添加好友搜索框的搜索结果页
1074
公众号会话下发的小程序消息卡片
1014
小程序模板消息
1043
公众号模板消息
1077
摇周边
1017
前往体验版的入口页
1044
带shareTicket的小程序消息卡片 详情
1078
连WiFi成功页
1019
微信钱包
1045
朋友圈广告
1079
微信游戏中心
1020
公众号profile页相关小程序列表
1046
朋友圈广告详情页
1081
客服消息下发的文字链
续表
场景值ID
说明
场景值ID
说明
场景值ID
说明
1022
聊天顶部置顶小程序入口
1047
扫描小程序码
1082
公众号会话下发的文字链
1023
安卓系统桌面图标
1048
长按图片识别小程序码
1084
朋友圈广告原生页
1024
小程序profile页
1049
手机相册选取小程序码
1089
微信聊天主界面下拉,“最近使用”栏(基础库2.2.4版本起包含“我的小程序”栏)
1025
扫描一维码
1052
卡券的适用门店列表
1090
长按小程序右上角菜单唤出最近使用历史
1026
附近小程序列表
1053
搜一搜的结果页
1091
公众号文章商品卡片
1027
顶部搜索框搜索结果页“使用过的小程序”列表
1054
顶部搜索框小程序快捷入口
1092
城市服务入口
1028
我的卡包
1056
音乐播放器菜单
1095
小程序广告组件
1029
卡券详情页
1057
钱包中的银行卡详情页
1096
聊天记录
1030
自动化测试下打开小程序
1058
公众号文章
1097
微信支付签约页
1031
长按图片识别一维码
1059
体验版小程序绑定邀请页
1099
页面内嵌插件
1102
公众号profile页服务预览
注意: 由于Android系统限制,目前还无法获取到按 Home 键退出到桌面,然后从桌面再次进小程序的场景值,对于这种情况,会保留上一次的场景值。
监听小程序生命周期变化的函数见表32。
表32小程序生命周期函数
属性
类型
描述
触 发 时 机
onLaunch()
Function
生命周期回调——监听小程序初始化
小程序初始化完成时(全局只触发一次)
onShow()
Function
生命周期回调——监听小程序显示
小程序启动或从后台进入前台显示时
onHide()
Function
生命周期回调——监听小程序隐藏
小程序从前台进入后台时
其中,onLaunch()函数的回调参数说明见表33。
表33onLaunch()函数回调参数说明
属性
类型
说明
path
String
启动小程序的路径
scene
Number
启动小程序的场景值
query
Object
启动小程序的 query 参数
续表
属性
类型
说明
shareTicket
String
当其他用户通过分享的小程序卡片打开小程序时,该字段包含转发的信息
referrerInfo
Object
来源信息。从另一个小程序、公众号或App进入小程序时返回。否则返回{}
onShow()函数的回调参数说明见表34。
表34onShow函数回调参数说明
属性
类型
说明
path
String
小程序切前台的路径
scene
Number
小程序切前台的场景值
query
Object
小程序切前台的query参数
ShareTicket
String
当其他用户通过分享的小程序卡片打开小程序时,该字段包含转发的信息
referrerInfo
Object
来源信息。从另一个小程序、公众号或App进入小程序时返回。否则返回{}
referrerInfo的结构说明见表35。
表35referrerInfo结构说明
属性
类型
说明
AppID
String
来源小程序、公众号或App的AppID
extraData
Object
来源小程序传过来的数据,scene=1037或1038时支持
当从表36中的场景进入小程序时,返回的referrerInfo才是有效的。
表36返回有效referrerInfo的场景
场景值
场景
AppID含义
1020
公众号profile页相关小程序列表
来源公众号
1035
公众号自定义菜单
来源公众号
1036
App分享消息卡片
来源App
1037
小程序打开小程序
来源小程序
1038
从另一个小程序返回
来源小程序
1043
公众号模板消息
来源公众号
3.2认识小程序页面的生命周期
【任务要求】
为当前示例项目的首页和查看日志页添加监听页面生命周期变化的函数,并在对应的生命周期处理函数中编写向调试器Console面板输出页面状态变化的代码。在首页跳转到日志页面时,需要携带名为message1,值为Hello以及名为message2,值为Logs的两个参数,并在日志页面输出获取到的参数。完成后在两个页面之间跳转,观察控制台输出,理解小程序页面生命周期的变化。
【任务分析】
除了小程序本身的生命周期外,小程序的每个页面也有自己的生命周期。相较于小程序的生命周期来说,每个页面的生命周期包含加载、准备、显示、隐藏和卸载这几个阶段。本次任务使用现有的两个页面之间的跳转,通过观察对应生命周期函数的输出来理解页面生命周期的变化。
【任务操作】
(1) 打开示例项目,在index页面的index.js文件中,修改onLoad函数,使其可以在调试器中输出运行的信息。新增用于监听页面生命周期变化的onShow()、onReady()、onHide()和onUnload()函数。同时修改bindViewTap()函数中的URL部分,使其带着参数message1和message2跳转到日志页面。完成后的index.js文件内容大致如下。
//index.js
Page({
data: {
…
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs?message1=Hello&message2=Logs'
})
},
onLoad: function () {
console.log("首页加载")
if (app.globalData.userInfo) {
…
}
},
getUserInfo: function(e) {
…
},
onShow: function () {
console.log("首页显示")
},
onReady: function () {
console.log("首页渲染完成")
},
onHide: function () {
console.log("首页隐藏")
},
onUnload: function () {
console.log("首页卸载")
}
})
(2) 打开logs.js,在onLoad()函数中新增一行代码用于输出首页传递过来的参数信息。同样新增用于监听页面生命周期变化的onShow(),onReady(),onHide()和onUnload()函数。完成后的logs.js函数大致如下。
//logs.js
Page({
data: {
…
},
onLoad: function (query) {
console.log("日志页加载", query)
this.setData({
…
})
})
},
onShow: function () {
console.log("日志页显示")
},
onReady: function () {
console.log("日志页渲染完成")
},
onHide: function () {
console.log("日志页隐藏")
},
onUnload: function () {
console.log("日志页卸载")
}
})
(3) 保存文件,编译项目。在首页上单击用户头像跳转到日志页面,观察如图33所示调试器的输出。
图33页面生命周期函数调用示例
【相关知识】
小程序页面的生命周期,会经历加载、显示、渲染完成、隐藏和卸载这几个过程,涉及的生命周期回调函数说明见表37。
表37页面生命周期回调函数
函数名
描述
onLoad()
页面加载时触发。一个页面只会调用一次,可以在onLoad()的参数中获取打开当前页面路径中的参数
onShow()
页面显示/切入前台时触发
onReady()
页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互
onHide()
页面隐藏/切入后台时触发。如navigateTo或底部Tab切换到其他页面,小程序切入后台等
onUnload()
页面卸载时触发。如redirectTo或navigateBack到其他页面时
在小程序中,所有页面的路由以栈的形式维护。当发生页面切换时,页面栈的变化见表38的说明。
表38各种情况下页面栈的表现
路 由 方 式
页面栈表现
初始化
新页面入栈
打开新页面
新页面入栈
页面重定向
当前页面出栈,新页面入栈
页面返回
页面不断出栈,直到目标返回页
Tab切换
页面全部出栈,只留下新的 Tab 页面
重加载
页面全部出栈,只留下新的页面
在上述路由方式中,每种情况下的路由触发方式以及对应页面响应的生命周期函数见表39。
表39路由触发时机和页面生命周期函数
路由方式
触 发 时 机
路由前页面
路由后页面
初始化
小程序打开的第一个页面
onLoad(), onShow()
打开新页面
调用wx.navigateTo接口或使用组件
onHide()
onLoad(), onShow()
页面重定向
调用wx.redirectTo接口或使用组件
onUnload()
onLoad(), onShow()
页面返回
调用wx.navigateBack接口或使用组件或用户单击左上角返回按钮
onUnload()
onShow()
Tab切换
调用wx.switchTab接口或使用组件或用户切换Tab
参考表310
重启动
调用wx.reLaunch接口或使用组件
onUnload
onLoad(), onShow()
Tab切换时对应页面的生命周期变化见表310(以A、B页面为tabBar页面,C是从A页面打开的页面,D页面是从C页面打开的页面为例)。
表310Tab切换页面的生命周期变化
当 前 页 面
路由后页面
触发的生命周期(按顺序)
A
A
A
B
A. onHide(), B.onLoad(), B.onShow()
A
B(再次打开)
A. onHide(), B.onShow()
C
A
C. onUnload(), A.onShow()
C
B
C. onUnload(), B.onLoad(), B.onShow()
D
B
D. onUnload(), C.onUnload(), B.onLoad(), B.onShow()
D(从转发进入)
A
D. onUnload(), A.onLoad(), A.onShow()
D(从转发进入)
B
D. onUnload(), B.onLoad(), B.onShow()
关于页面的路由,需要注意以下几点。
(1) navigateTo(),redirectTo()只能打开非tabBar页面。
(2) switchTab()只能打开tabBar页面。
(3) reLaunch()可以打开任意页面。
(4) 页面底部的tabBar由页面决定,即只要是定义为tabBar的页面,底部都有tabBar。
tabBar部分可以参见2.1节内容,涉及的接口可以参见14.7节的内容,组件可以参考9.1节的内容。
在本次任务中,可以看到,加载首页的时候依次执行的函数是onLoad(),onShow()和onReady()。在单击首页头像后,首页被隐藏,首页的onHide()函数被执行,然后跳转到日志页面。日志页面的onLoad()函数运行,同时也获取到了首页传递过来的参数message1和message2。紧接着,日志页面的onShow()和onReady()函数被执行。
在index.js文件中,为要跳转到的logs页面地址添加了message1和message2两个参数。路径携带参数的格式要求为: 参数与路径之间使用“?”分隔,参数键与参数值之间用“=”相连,不同参数用“&”分隔,例如'path?key=value&key2=value2'。
结合小程序的生命周期和页面的生命周期,在小程序运行的过程中,各个生命周期函数的调用顺序可参考如图34所示说明。
图34小程序和页面生命周期变化
3.3概览MINA框架
【任务要求】
在pages目录下新建一个Chapter_3目录,在Chapter_3目录下新建一个名为mina的页面。要求在页面上显示文本“This is a text”和一个Change Text按钮,并实现单击按钮后将文本更改为“This is another text”的功能。
【任务分析】
MINA是小程序使用的框架,其核心是一个响应的数据绑定系统。本次任务通过实现一个简单的单击按钮更改文字的功能,来体会框架中视图层和逻辑层的联系。
【任务操作】
(1) 打开示例项目,在app.json文件的pages数组中,新增第一项"pages/Chapter_3/mina/mina"。保存文件,编译项目,可以看到开发者工具已经建好了mina页面,模拟器中正在运行的也是mina页面。完成后的目录结构如图35所示。
图35新建mina页面后的项目目录结构
(2) 打开mina.js文件,在Page函数的页面初始数据处,添加一个名为text,值为“This is a text”的初始数据。完成后的mina.js文件内容大致结构如下。
// pages/Chapter_3/mina/mina.js
Page({
/**
* 页面的初始数据
*/
data: {
text:"This is a text"
},
…
})
(3) 打开mina.wxml文件,删除原文件内容,新增用于显示mina.js文件中text变量的代码,同时增加一个按钮,在按钮的属性中,为单击事件绑定一个名为changeText()的函数。完成后的mina.wxml文件内容如下。
{{text}}
(4) 回到mina.js文件,需要为按钮单击事件的处理函数changeText()编写代码逻辑,实现更改文字的功能。完成后的mina.js文件内容结构大致如下。
// pages/Chapter_3/mina/mina.js
Page({
/**
* 页面的初始数据
*/
data: {
text:"This is a text"
},
…
/**
* 用户单击右上角分享
*/
onShareAppMessage: function () {
},
changeText:function(){
this.setData({
text:"This is another text"
})
}
})
(5) 保存所有文件,编译项目,在模拟器中查看mina页面表现,单击按钮,观察文字是否被改变。正常情况下,页面表现应如图36所示。
图36mina页面单击按钮前(左)和单击按钮后(右)
【相关知识】
MINA框架(后文简称为“框架”)是微信小程序的开发框架。框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生应用体验的服务。
框架中,整个小程序的内容分为两个层次: 视图层(View)和逻辑层(App Service)。视图层是小程序的“外观”,它规定了小程序页面的结构和样式,决定了小程序内容的展示; 逻辑层是小程序的“内涵”,它控制了小程序的生命周期和数据处理等。除了处理本地的业务逻辑之外,在开发小程序的过程中逻辑层往往还需要通过HTTP同远程服务器进行数据交流。
框架提供了自己的视图层描述语言规范: WXML和WXSS,以及基于JavaScript的逻辑层,并在视图层与逻辑层间提供了数据传输和事件系统,可以让开发者将重点放在数据与视图上。
整个框架中最核心的部分就是视图层和逻辑层之间的数据绑定和事件响应系统。视图层中的信息通过事件绑定携带参数向逻辑层传递,逻辑层的数据通过数据绑定向视图层传递。框架可以让数据与视图非常简单地保持同步。当作数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
在本次任务中,通过框架将逻辑层(mina.js文件)数据中的text与视图层(mina.wxml文件)的text进行了绑定,所以在页面一打开的时候会显示“This is a text”。当单击按钮的时候,触发了按钮的单击事件,视图层会发送单击事件的处理函数changeText()给逻辑层,逻辑层找到并执行对应的函数。changeText()函数触发后,逻辑层执行setData的操作,将data中的text从“This is a text”变为“This is another text”,因为该数据和视图层已经绑定了,从而视图层上显示的内容会自动改变为“This is another text”。
框架除了最为核心的数据绑定和事件响应系统外,还管理了整个小程序的页面路由,可以做到页面间的无缝切换,并给予页面完整的生命周期。开发者需要做的只是将页面的数据、方法、生命周期函数注册到框架中,其他的一切复杂的操作都交由框架处理。有关页面生命周期和路由的介绍,可以参见3.2节内容。
框架提供了一套基础的组件,这些组件自带微信风格的样式以及特殊的逻辑,开发者可以通过组合基础组件,创建出强大的微信小程序。大量的预置组件如文本组件、轮播组件等使开发者能够专注业务逻辑的编写,快速开发出需要的小程序。
框架还提供了丰富的微信原生API,使用这些API可以方便地调用微信提供的能力,如获取用户信息、本地存储、支付等。
3.4逻辑层
小程序开发框架的逻辑层使用JavaScript引擎为小程序提供开发者JavaScript代码的运行环境以及微信小程序的特有功能。
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。开发者写的所有代码最终将会打包成一份JavaScript文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似Service Worker,所以逻辑层也称为App Service。
在JavaScript的基础上,微信小程序还增加了一些新的功能,以方便小程序的开发。
(1) 增加App()和Page()方法,进行程序和页面的注册。
(2) 增加getApp()和getCurrentPages()方法,分别用来获取App实例和当前页面栈。
(3) 提供丰富的API,如微信用户数据、扫一扫、支付等微信特有能力。
(4) 每个页面有独立的作用域,并提供模块化能力。
需要注意的是,小程序框架的逻辑层并非运行在浏览器中,因此JavaScript在Web中的一些能力都无法使用,如window、document等。
3.4.1注册程序
【任务要求】
新建一个临时的项目,要求在建立的时候,不勾选任何启动模板,直接建立一个完全空白的项目,然后手动新建必要的app.js以及app.json文件。除了需要在app.js文件中注册小程序所有的生命周期函数以外,还需要注册错误监听函数以及页面不存在监听函数,同时,还需要在app.js中设置一个名为globalData,值为“This is global data”字符串的变量。在app.json文件中,注册一个名为demo的页面,并让该页面显示globalData的值。
【任务分析】
注册程序,意即让开发者工具知道当前的目录和文件是一个小程序并将其作为小程序进行处理的步骤。之前的任务,我们一直都是使用基于“普通快速启动模板”(详见1.3节)建立的小程序项目。本次任务,需要自己手动从一个空白的目录建立小程序,并熟悉声明注册一个小程序的过程,体会哪些文件是小程序必需的。
【任务操作】
(1) 新建一个名为Blank_Project的项目,具体操作如图37所示。注意不要勾选图中的“建立普通快速启动模板”复选框。
图37新建空白小程序项目
(2) 新建app.js文件和app.json文件。app.js的文件内容如下。
//app.js
App({
onLaunch: function (options) {
console.log("小程序启动",options)
},
onShow: function (options) {
console.log("小程序显示",options)
},
onHide: function (options) {
console.log("小程序隐藏",options)
},
onError: function (msg) {
console.log("小程序发生错误",msg)
},
onPageNotFound:function(msg){
console.log("小程序要打开的页面不存在",msg)
},
globalData: 'This is global data'
})
app.json文件内容如下。
{
"pages": [
"Demo/demo"
]
}
保存文件,编译项目,完成后的项目目录结构如图38所示。
图38Blank_Project项目目录
(3) 打开demo.js文件,在第一行使用getApp()函数获取小程序实例,在data中,将小程序实例中的globalData数据赋给页面的message变量。随后打开demo.wxml文件,在标签中使用{{message}}来显示demo.js中message变量的值。完成后的demo.js文件大致如下。
// Demo/demo.js
const appInstance = getApp()
Page({
/**
* 页面的初始数据*/
data: {
message:appInstance.globalData
},
…
})
demo.wxml文件内容如下。
{{message}}
(4) 编译运行,可以看到在模拟器中显示出了demo页面的内容(如图39所示),调试区输出了小程序生命周期的相关内容(如图310所示)。
图39模拟器页面输出
图310调试区输出
【相关知识】
在本次任务中,最为重要的便是app.js文件中的App(Object)函数。App()函数用来注册一个小程序。接受一个Object参数,用于指定小程序的生命周期回调等。App()必须在app.js中调用,必须调用且只能调用一次,不然会出现无法预期的后果。
Object参数说明见表311。
表311App函数参数说明
属性
类型
描述
触 发 时 机
onLaunch()
Function
生命周期回调—监听小程序初始化
小程序初始化完成时(全局只触发一次)
onShow()
Function
生命周期回调—监听小程序显示
小程序启动或从后台进入前台显示时
onHide()
Function
生命周期回调—监听小程序隐藏
小程序从前台进入后台时
onError()
Function
错误监听函数
小程序发生脚本错误,或者API调用失败时触发,会带上错误信息
onPageNotFound()
Function
页面不存在监听函数
小程序要打开的页面不存在时触发,会带上页面信息回调该函数
其他
Any
开发者可以添加任意的函数或数据到Object参数中,用this可以访问
其中,有关小程序生命周期函数的相关内容可以参考3.1节。
onError(String error)函数会在小程序发生脚本错误或API调用报错时触发。string error表示错误信息,包含堆栈信息。
onPageNotFound(Object)函数会在小程序要打开的页面不存在时触发。其参数说明见表312。
表312onPageNotFound函数参数说明
属性
类型
说明
path
String
不存在页面的路径
query
Object
打开不存在页面的 query 参数
isEntryPage
Boolean
是否本次启动的首个页面(例如从分享等入口进来,首个页面是开发者配置的分享页面)
一个简单的使用示例如下。
App({
onPageNotFound(res) {
wx.redirectTo({
url: 'pages/...'
}) // 如果是 tabBar 页面,请使用 wx.switchTab
}
})
当小程序页面不存在时,需要注意以下几点。
(1) 开发者可以在回调中进行页面重定向,但必须在回调中同步处理,异步处理(例如setTimeout异步执行)无效。
(2) 若开发者没有处理页面不存在的情况,当跳转页面不存在时,将打开微信客户端原生的页面不存在提示页面。
(3) 如果回调中又重定向到另一个不存在的页面,将打开微信客户端原生的页面不存在提示页面,并且不再第二次回调。
getApp(Object)是一个全局函数,可以用来获取到小程序App实例。一个简单的使用getApp函数的示例如下。
// other.js
const appInstance = getApp()
console.log(appInstance.globalData)// I am global data
使用getApp()函数需要注意以下两点。
(1) 不要在定义于App()内的函数中调用getApp(),使用this就可以得到App实例。
(2) 通过getApp()获取实例之后,不要私自调用生命周期函数。
3.4.2注册页面
【任务要求】
打开之前的示例项目(非上个任务的空白项目),在pages/Chapter_3文件夹下新建一个名为page的文件夹,并在里面新建一个名为page的页面,要求如下。
(1) 当页面显示时,在调试器的Console面板输出当前页面的路径;
(2) 添加页面的初始数据,分别包括文本、数字、JSON对象和数组四种数据类型;
(3) 在页面上显示所有的初始数据,同时添加4个按钮用于修改其内容;
(4) 添加一个按钮用于新增一个数据并在页面上显示出来;
(5) 设置自定义分享的标题,将当前页面分享给他人。
完成后的页面示例如图311所示。
图311初始页面(左)和单击按钮后页面(右)
【任务分析】
对页面的相关处理,可以说是在开发小程序的时候打交道最多的操作了。同注册程序需要一个App()函数一样,注册页面也需要一个Page()函数。通过设置Page()函数中的参数,可以实现对页面生命周期的处理,监听页面的事件和处理组件事件。同时,注册程序里面还有一个非常重要的用于逻辑层和视图层数据同步的机制。这些都是十分重要的。
【任务操作】
(1) 打开示例项目,在app.json文件的pages数组中,新增第一项"pages/Chapter_3/page/page"。保存文件,编译项目,让开发者工具自动生成page页面所需的文件。
(2) 打开page.wxml,将其中的内容替换成如下代码。
{{text}}
{{num}}
{{array[0].text}}
{{object.text}}
{{newField.text}}
(3) 打开page.js,将其中的内容替换成如下代码。
// pages/Chapter_3/page/page.js
Page({
data: {
text: 'init data',
num: 0,
array: [{ text: 'init data' }],
object: {
text: 'init data'
}
},
onShow(){
console.log(this.route)
},
changeText() {
// this.data.text = 'changed data' // 不要直接修改 this.data
// 应该使用 setData
this.setData({
text: 'changed data'
})
},
changeNum() {
// 或者,可以修改 this.data 之后马上用 setData 设置修改了的字段
this.data.num = 1
this.setData({
num: this.data.num
})
},
changeItemInArray() {
// 对于对象或数组字段,可以直接修改一个其下的子字段,这样做通常比修改整个对象或数组更好
this.setData({
'array[0].text': 'changed data'
})
},
changeItemInObject() {
this.setData({
'object.text': 'changed data'
})
},
addNewField() {
this.setData({
'newField.text': 'new data'
})
},
onShareAppMessage(res){
if (res.from === 'menu') {
// 来自右上角转发菜单
console.log(res.target)
}
return {
title: '注册页面示例',
path: this.route
}
}
})
(4) 保存所有文件,编译项目并运行。可以看到页面的表现和按钮的作用如图311所示。观察调试器的Console面板,可以看到如图312所示输出的当前页面路径信息。
图312onShow函数中使用this.route输出当前页面路径
(5) 在模拟器区单击右上角的“菜单”按钮,在弹出的选项中选择“转发”(如图313所示),可以看到带自定义标题的用当前页面截图生成的转发卡片(如图314所示),同时可以看到在Console面板中多了一行如图315所示的输出。
图313“转发”选项
图314转发卡片预览
图315输出转发信息
【相关知识】
Page(Object)函数用来注册一个页面。它接受一个Object类型参数,用于指定页面的初始数据、生命周期回调、事件处理函数等。其中,Object参数内容说明见表313。
表313Page函数参数说明
属性
类型
描述
data
Object
页面的初始数据
onLoad()
Function
生命周期回调—监听页面加载
onShow()
Function
生命周期回调—监听页面显示
onReady()
Function
生命周期回调—监听页面初次渲染完成
onHide()
Function
生命周期回调—监听页面隐藏
onUnload()
Function
生命周期回调—监听页面卸载
onPullDownRefresh()
Function
监听用户下拉动作
onReachBottom()
Function
页面上拉触底事件的处理函数
onShareAppMessage()
Function
用户单击右上角“转发”菜单
onPageScroll()
Function
页面滚动触发事件的处理函数
onResize()
Function
页面尺寸改变时触发,详见响应显示区域变化
onTabItemTap()
Function
当前是tab页时,单击tab时触发
其他
Any
开发者可以添加任意的函数或数据到Object参数中,在页面的函数中用this可以访问
其中,data是页面第一次渲染使用的初始数据。页面加载时,data将会以JSON字符串的形式由逻辑层传至渲染层,因此data中的数据必须是可以转成JSON的类型: 字符串,数字,布尔值,对象,数组。在渲染层的wxml文件中,可以使用“{{var}}”的方式绑定数据。一个简单的示例片段如下。
{{text}}
{{array[0].msg}}
//example.js
Page({
data: {
text: 'init data',
array: [{msg: '1'}, {msg: '2'}]
}
})
在上面这个示例片段中,最终页面上会输出字符串init data和1。
对页面的生命周期函数的介绍和页面的路由跳转,请参见表37的相关内容。此处需要单独提到的一点是,onLoad()函数带有一个Object类型的名为query的参数,该参数包含打开当前页面路径中的参数信息。例如,一个页面通过在路径中使用“url/?key=value”的方式携带了参数跳转到当前页面,那么在当前页面的onLoad函数加载时,便可以在其query参数中获取到以“key : value”形式存储的JSON数据对象。
onPullDownRefresh函数用于监听用户的下拉刷新事件。需要注意的是,下拉刷新的功能并不是默认启用的,如果要允许用户使用下拉刷新,可以在app.json的window选项中或页面配置中设置enablePullDownRefresh的值为true。也可以使用wx.startPullDownRefresh触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。当处理完数据刷新后,调用wx.stopPullDownRefresh可以停止当前页面的下拉刷新。
onReachBottom()函数用于监听用户上拉触底事件。开发者可以在app.json的window选项中或页面配置中设置触发距离onReachBottomDistance,设置好后,用户滑到距离页面底部指定距离时,便会执行onReachBottom函数中的内容。用户在触发距离内滑动期间,本事件只会被触发一次。
onPageScroll(Object)用于监听用户滑动页面事件。其Object参数说明见表314。
表314onPageScroll函数参数说明
属性
类型
说明
scrollTop
Number
页面在垂直方向已滚动的距离(单位为px)
需要注意的是,只在需要的时候才在page中定义此方法,不要定义空方法,以减少不必要的事件派发对渲染层和逻辑层通信的影响。同时需要避免在onPageScroll中过于频繁地执行setData等引起逻辑层到渲染层通信的操作,尤其是每次传输大量数据时,这样会影响通信耗时。
onShareAppMessage(Object) 用于监听用户单击页面内“转发”按钮(