微信小程序从入门到精通(二) 小程序的能力
上一篇blog介绍了小程序的一些基础概念和代码构成以及主要的几个文件类型( 微信小程序从入门到精通(一) 基础知识与代码构成),那么本篇blog就继续围绕官方的简易教程以及QuickStart项目来继续研究一下小程序如何工作。
上次我们具体了解小程序的代码构成以及四个重要的文件类型(wxml、wxss、js和app.json),那么接下来就看看小程序的整体工作运行流程,首先从启动开始。
首先来看一下小程序启动后首先会发生什么事情,引用官方文档的说明:
小程序启动之后,在 app.js 定义的 App 实例的 onLaunch 回调会被执行。
App({ onLaunch: function () { // 小程序启动之后 触发 } })12345
如上所示,这里又提到了一个文件——app.js,之前我们了解了app.json(全局配置,包含页面路径等)和app.wxss(全局样式),那么同理app.js也是一个用于描述公共逻辑代码的文件,是小程序中必须的两个主配置文件之一(app.json和app.js):
这里暂且只需要明确一个小程序的主体必定是有3个文件构成的,分别是app.js(必须)、app.json(必须)和app.wxss(可选),明确了这一点之后我们继续来看刚才官方文档中提到的App实例,也就是在上面的代码中看到的App{()}
,这里就牵扯到了另一个重要概念——注册程序,属于逻辑层的操作(App Service),官方文档中有这样一句话:
增加 App 和 Page 方法,进行程序和页面的注册。
如上所示,Page方法我们之前已经见过了,WXML 中的动态数据均来自对应 Page的data,用于注册一个页面,关于Page我们后面再说,但我们需要知道App和Page方法是逻辑层(App Service)最核心的两个方法,接下来我们首先具体研究一下App方法,引用官方文档的概述:
App() 函数用来注册一个小程序。接受一个 object 参数,其指定小程序的生命周期函数等。
如上所示,object参数我们需要注意,在上面提到的onLaunch 回调就是这个object参数之一,上面示例代码的注释中说onLaunch会在小程序启动后触发,那么它会被调用几次?依旧从官方文档中来寻找答案,我们看一下App函数的object参数的所有属性:
属性 | 类型 | 描述 | 触发时机 |
---|---|---|---|
onLaunch | Function | 生命周期函数–监听小程序初始化 | 当小程序初始化完成时,会触发 onLaunch(全局只触发一次) |
onShow | Function | 生命周期函数–监听小程序显示 | 当小程序启动,或从后台进入前台显示,会触发 onShow |
onHide | Function | 生命周期函数–监听小程序隐藏 | 当小程序从前台进入后台,会触发 onHide |
onError | Function | 错误监听函数 | 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息 |
其他 | Any | 开发者可以添加任意的函数或数据到 Object 参数中,用 this 可以访问 |
如上所示,可以看到onLaunch函数主要是做小程序的全局初始化工作,因为只会触发一次,类似于在android开发中的application中设置一个静态方法,在上面的表格中注意下描述列,onLaunch是一个生命周期函数,所以这里有必要再具体说明一下微信小程序中关于生命周期的一些概念,小程序的生命周期需要分为两部分来说,分别是:
应用的生命周期
页面的生命周期
这就不像在android中生命周期仅针对于界面(Activity、Fragment),在小程序的官方文档中并没有对生命周期做单独介绍,而是以注册程序和注册页面这两个标题来介绍了App()函数和Page()函数,然后分别在各自(App、Page)的object参数说明中以上面的表格的形式简单标记出了哪些是生命周期函数,关于Page后面再说,先回归正题,App()很明显有3个生命周期函数,分别是:
onLaunch
onShow
onHide
如上所示,除了onLaunch仅仅会执行一次之外,onShow和onHide都会根据生命周期的变化被多次执行,注意下在onShow触发时机中提到了一个前台和后台的概念,其实很简单,跟android的那几个容易混淆的onResume、onPause、onStop等等相比容易多了,引用官方文档的原话:
当用户点击左上角关闭,或者按了设备 Home 键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。
就是这么简单,那么小程序什么时候会被销毁?官方文档是这样说的:
只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。
先简单了解一下,至于具体在什么场景销毁,我们以后再做详细说明,否则一次牵引出太多的点容易导致学习流程没有主线,再次回到我们的主题onLaunch函数,QuickStart项目中的onLaunch内容略微有些复杂,我们简单看一下:
//app.jsApp({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 登录 wx.login({ success: res => { // 发送 res.code 到后台换取 openId, sessionKey, unionId } }) // 获取用户信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 wx.getUserInfo({ success: res => { // 可以将 res 发送给后台解码出 unionId this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 if (this.userInfoReadyCallback) { this.userInfoReadyCallback(res) } } }) } } }) }, globalData: { userInfo: null } })123456789101112131415161718192021222324252627282930313233343536373839
根据注释我们可以大概看出来在onLaunch函数中做了几件事情,我们以第一个“展示本地存储能力”为例,详细分析一下4~7行的代码:
// 展示本地存储能力var logs =wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs)1234
如上所示,调用了wx.getStorageSync和wx.setStorageSync这2个API,这就是小程序为我们提供的本地存储API,早期的移动web开发中本地存储只能用cookie的方式解决,但是cookie大小限制在4K,而且某些浏览器还存在cookie个数限制,而后随着H5的发展本地存储可以通过localStorage这个东东解决,但也仅是IE8以后才能支持,大小有5M,这就解决了很大一部分的存储容量问题,而我们微信小程序的官方文档中说了:
同一个微信用户,同一个小程序 storage 上限为 10MB。
OK,本地缓存容量上又进一步提升了,现在看一下wx.getStorageSync和wx.setStorageSync的说明以及用法:
wx.setStorageSync(KEY,DATA)
将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。
wx.getStorageSync(KEY)
从本地缓存中同步获取指定 key 对应的内容。
如上所示,使用方法很简单,同android的SharedPreferences用法类似,都是以key/value的形式去存取数据,注意这两个是同步接口,API中也有两个异步的版本(wx.setStorage和wx.getStorage),这两个异步接口参数相对就多一些了,以后再说,回到代码中来,现在看这3行代码就比较清晰了,首先通过获取本地缓存中key为logs所对应的值,如果为0或者null(首次启动)就返回一个空数组,然后在这个空数组中追加一个元素(当前时间),最后将当前时间存入本地缓存,key为logs。说白了这就是记录一个启动日志,首次启动会创建一个新数组,之后每次启动都会给这个数组追加当前时间,所以我们在模拟器中的index页面点击头像后跳转到logs页面看到的时间列表就是从缓存读取的:
关于onLaunch函数还有一点需要说的就是它默认是带有一个参数的,包括onShow方法:
App({ onLaunch: function(options) { // Do something initial when launch. }, onShow: function(options) { // Do something when show. }, onHide: function() { // Do something when hide. }, onError: function(msg) { console.log(msg) }, globalData: 'I am global data'})123456789101112131415
如上所示,那么这个options参数都包含哪些属性呢?官方文档列出了7个属性(path、query、scene、shareTicket、referrerInfo、referrerInfo.appId、referrerInfo.extraData),下面只列举2个先简单了解下,我们在onLaunch函数中打印一下options的path和sence属性:
path是打开小程序的路径,scene是打开小程序的场景值,关于场景值的概念以后再说,此处简单了解一下即可,接下来要说一个比较重要的函数getApp()
,它可以用来获取到小程序实例,例如获取全局变量globalData:
// other.jsvar appInstance = getApp() console.log(appInstance.globalData) // I am global data123
如上所示,关于小程序启动相关的内容先说这么多,接下来看一下页面相关的内容。
接下来要说的就是本篇blog的重点内容了,注册页面,也就是关于Page函数。上一篇blog我们简单介绍了一部分关于Page的内容,下面看一下官方文档简易教程中对Page的概述和一段示例代码:
Page 是一个页面构造器,这个构造器就生成了一个页面。在生成页面的时候,小程序框架会把 data 数据和 index.wxml 一起渲染出最终的结构,于是就得到了你看到的小程序的样子。在渲染完界面之后,页面实例就会收到一个 onLoad 的回调,你可以在这个回调处理你的逻辑。
Page({ data: { // 参与页面渲染的数据 logs: [] }, onLoad: function () { // 页面渲染后 执行 } })12345678
如上所示,之所以说Page重要,是因为它的作用的构造页面,而我们的小程序就是通过一个个页面组成的,Page函数的2个核心点分别是data和onLoad回调,data负责提供数据,onLoad则是处理页面渲染后的逻辑。接下来我们就具体看一下Page函数,我们在IDE中新建一个js文件后写一个字母P首先看到的就是Page函数的自动补全提示:
如上图,紧接着会自动生成一个完整的Page函数模板,代码如下:
Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } })1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
注释也都比较清晰,可以看到Page函数有5个生命周期函数(onLoad、onReady、onShow、onHide、onUnload),相比较App函数(onLaunch、onShow、onHide)多出了2个,同样的Page函数也支持我们自定义函数或者数据,就如同上一篇blog中写的那个clickMe函数,下面我们按顺序依次了解一下,首先是data,data在官方文档中的定义是初始化数据,文档中是这样描述的:
初始化数据将作为页面的第一次渲染。data 将会以 JSON 的形式由逻辑层传至渲染层,所以其数据必须是可以转成 JSON 的格式:字符串,数字,布尔值,对象,数组。
如上所示,这里我们注意两点,分别是第一次渲染和JSON格式。第一次渲染的意思就是在onLoad函数被调用之前的页面渲染,而JSON格式则是定死了data中的数据类型只能是以下几种(String,Number,Boolean,Object,Array),下面看一下QuickStart项目中Page函数的data部分:
Page({ data: { motto: 'Hello World', userInfo: {}, hasUserInfo: false, canIUse: wx.canIUse('button.open-type.getUserInfo') } })12345678
如上所示,这里定义的初始化数据比较简单,字符串motto直接通过wxml中的{{motto}}渲染在页面并显示,Object类型的用户信息(userInfo),具体讲是一个JSON对象,这里初始化为空,因为会在onLoad函数中赋值,Boolean类型的是否存在用户信息标志(hasUserInfo)以及Boolean类型的“判断小程序的API,回调,参数,组件等是否在当前版本可用”的标志。看完了data接下来看一下更重要的生命周期函数,首先是最重要的onLoad函数,它与App()中的onLaunch函数相对应,后者是小程序启动后全局触发一次,前者则是一个页面只会调用一次,所以这个onLoad也就相当于android中Activity的onCreate,依旧看一下QuickStart项目中的onLoad函数,由于index.js中的onLoad略微复杂,我们先以log.js中的onLoad函数为例:
Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(log => { return util.formatTime(new Date(log)) }) }) } })123456789101112
这个例子就很好,用到了我们上面提到过的本地缓存(wx.getStorageSync)和data,可以看到data中定义了一个空数组,然后在onLoad函数中做了赋值操作(setData),所以这里我们可以再仔细体会一遍data和onLoad各自的作用和关系。说到了setData,下面就详细说明一下这个函数Page.prototype.setData()
,首先看一下官方文档中的概述:
setData 函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)。
如上所示,可以看到setData函数做了两件事情,首先是把数据以异步形式发送到视图层,也就是说可以理解为对页面进行的第二次渲染,然后以同步形式改变了this.data的值,也就是在这里同步修改了初始化数据,这里再强调一点,同App()函数一样,Page()函数的参数也支持任意类型的自定义函数或数据,同样是通过this调用,下面看一下官方文档给出的几个小例子:
//index.jsPage({ data: { text: 'init data', num: 0, array: [{text: 'init data'}], object: { text: 'init data' } }, changeText: function() { // this.data.text = 'changed data' // bad, it can not work this.setData({ text: 'changed data' }) }, changeNum: function() { this.data.num = 1 this.setData({ num: this.data.num }) }, changeItemInArray: function() { // you can use this way to modify a danamic data path this.setData({ 'array[0].text':'changed data' }) }, changeItemInObject: function(){ this.setData({ 'object.text': 'changed data' }); }, addNewField: function() { this.setData({ 'newField.text': 'new data' }) } })123456789101112131415161718192021222324252627282930313233343536373839
注意下第12行的注释代码,不能直接通过this.data.key = “xxx”去改变data中的数据,必须要通过setData方法才能改变。在上面的例子中我们依次列举了修改以下几种类型的数据:
字符串 line11~16
数字 line17~22
数组 line23~28
对象 line29~38
如上所示,需要注意的是再修改data为Object类型的数据时,setData中参数对象的key必须是字符串形式,例如'object.text': 'changed data'
,说了这么多关于Page的生命周期函数也只讲了1个——onLoad,关于剩下的几个非重点函数(onShow、onReady、onHide和onUnload)以后再说,否则会导致篇幅过长。跳过这几个生命周期函数,那我们的Page函数还剩下几个事件处理函数,我们以下拉刷新(onPullDownRefresh)和上拉触底(onReachBottom)为例详细说明一下。依旧先看一下官方文档对这两个函数的详细说明:
如上所示,首先是下拉刷新,很简单,监听用户下拉刷新动作,上一篇blog也提到了需要在app.json中配置开启下拉刷新后才行,看以一个简单的例子:
Page({ onPullDownRefresh:function(){ wx.showToast({ title: '执行了下拉刷新动作~', icon: 'none', duration: 2000 }) } })123456789
这里又用到了一个简单的交互反馈API——显示消息提示框,跟android中的吐司基本一样,用法也很简单,title是提示内容,duration是延迟时间(单位毫秒),icon是图标,可以设置为“success”、“loading”和“none”,下面看一下下拉的效果图:
如上所示,而上拉触底则需要在scroll-view组件中去滑动才能触发诸如滚动条滚动、触底、触顶等事件,牵扯到组件的问题我们后面再说。
本篇blog重点介绍了注册程序(App函数)和注册页面(Page函数)的相关用法以及setData函数的用法,主要还停留在基础理性的部分,等后面学习了组件之后就可以尝试做一些东西了,由于以前js基础不是特别好所以在学习过程中会遇到各种各样的js语法糖,那就开开心心的当补习js基础知识好了!先到这里吧,The End。