[TOC]
## 2.2 程序与页面
从逻辑组成来说,一个小程序是由多个`页面`组成的`程序`。
而通常`程序`在启动或者退出的时候存储数据或者在“页面”显示或者隐藏的时候做一些逻辑处理,
### 2.2.1 程序
宿主环境(客户端)提供了`App()` 构造器用来注册一个程序App
>[warning] `App()` 构造器必须写在项目根目录的app.js文件里
App实例是单例对象,在其他JS脚本中可以使用宿主环境提供的 `getApp()` 来获取程序实例。
代码清单3-3 getApp() 获取App实例
~~~
// other.js
var appInstance = getApp()
~~~
#### 2.2.1.1 程序构造器--`App()`
App() 的调用方式如代码清单3-4所示,App构造器接受一个Object参数,参数说明如表3-1所示,其中onLaunch / onShow / onHide 三个回调是App实例的生命周期函数,我们会在后文展开;onError我们暂时不在本章展开,我们会在第8章里详细讨论;App的其他参数我们也放在后文进行展开。
代码清单3-4 App构造器
~~~
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
~~~
:-: 表3-1 App构造器的参数
| 属性 | 类型 | 描述 | 触发时机 |
| --- | --- | --- | --- |
| onLaunch | Function | 生命周期函数--监听小程序初始化 | 当小程序初始化完成时,会触发 onLaunch(全局只触发一次) |
| onShow | Function | 生命周期函数--监听小程序显示 | 当小程序启动,或从后台进入前台显示,会触发 onShow|
| onHide | Function | 生命周期函数--监听小程序隐藏 | 当小程序从前台进入后台,会触发 onHide |
| onError | Function | 错误监听函数 | 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息 |
| onPageNotFound | Function | 页面不存在监听函数 | 当小程序出现要打开的页面不存在的情况,会带上页面信息回调该函数|
| 其他 | Any | | 开发者可以添加任意的函数或数据到 Object 参数中,用 this 可以访问|
`getApp()`,全局的 getApp() 函数可以用来获取到小程序实例。
>[warning] 1. App() 必须在 app.js 中注册,且不能注册多个。
> 2. 不要在定义于 App() 内的函数中调用 getApp() ,使用 this 就可以拿到 app 实例。
> 3. 不要在 onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。
> 4. 通过 getApp() 获取实例之后,不要私自调用生命周期函数。
#### 2.2.1.2 程序的生命周期和打开场景
* **前台、后台定义**: 当用户点击左上角关闭,或者按了设备 Home 键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。需要注意的是:只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。
初次进入小程序的时候,微信客户端初始化好宿主环境,同时从网络下载或者从本地缓存中拿到小程序的代码包,把它注入到宿主环境,初始化完毕后,微信客户端就会给App实例派发onLaunch事件,App构造器参数所定义的`onLaunch`方法会被调用。
进入小程序之后,用户可以点击左上角的关闭,或者按手机设备的Home键离开小程序,此时小程序并没有被直接销毁,我们把这种情况称为“小程序进入后台状态”,App构造器参数所定义的`onHide`方法会被调用。
当再次回到微信或者再次打开小程序时,微信客户端会把“后台”的小程序唤醒,我们把这种情况称为“小程序进入前台状态”,App构造器参数所定义的`onShow`方法会被调用。
>[warning] App的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该从其他代码里主动调用App实例的生命周期函数。
在微信客户端中打开小程序有很多途径:
* 从群聊会话里打开
* 从小程序列表中打开
* 通过微信扫一扫二维码打开
* 从另外一个小程序打开当前小程序等
针对不同途径的打开方式,小程序有时需要做不同的业务处理,所以微信客户端会把打开方式带给onLaunch和onShow的调用参数`options`,示例代码以及详细参数如代码清单3-5和表3-2所示。需要留意小程序的宿主环境在迭代更新过程会增加不少打开场景,因此要获取最新的场景值说明请查看[【官方文档】](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/app.html)。
代码清单3-5 onLaunch和onShow带参数
~~~
App({
onLaunch: function(options) { console.log(options) },
onShow: function(options) { console.log(options) }
})
~~~
:-: 表3-2 `onLaunch`,`onShow`参数
| 字段 | 类型 | 描述 |
| --- |--- | ---|
| path | String | 打开小程序的页面路径 |
| query | Object | 打开小程序的页面参数query |
| scene | Number | 打开小程序的场景值,详细场景值请参考小程序官方文档 |
| shareTicket | String | shareTicket,详见小程序官方文档 |
| referrerInfo | Object | 当场景为由从另一个小程序或公众号或App打开时,返回此字段 |
| referrerInfo.appId | String | 来源小程序或公众号或App的 appId,详见下方说明 |
| referrerInfo.extraData | Object | 来源小程序传过来的数据,scene=1037或1038时支持 |
:-: 表3-3 以下场景支持返回 referrerInfo.appId
| 场景值 | 场景 | appId信息含义 |
| --- |--- | ---|
| 1020 | 公众号 profile | 页相关小程序列表 返回来源公众号 appId
| 1035 | 公众号自定义菜单 | 返回来源公众号 appId
| 1036 | App 分享消息卡片 | 返回来源应用 appId
| 1037 | 小程序打开小程序 | 返回来源小程序 appId
| 1038 | 从另一个小程序返回 | 返回来源小程序 appId
| 1043 | 公众号模板消息 | 返回来源公众号 appId
`onPageNotFound`
> 基础库 1.9.90 开始支持,低版本需做兼容处理
当要打开的页面并不存在时,会回调这个监听器,并带上以下信息:
| 字段 | 类型 | 说明 |
| --- |--- | ---|
| path | String | 不存在页面的路径 |
| query | Object | 打开不存在页面的 query |
| isEntryPage | Boolean | 是否本次启动的首个页面 (例如从分享等入口进来,首个页面是开发者配置的分享页面)|
开发者可以在 `onPageNotFound` 回调中进行重定向处理,但必须在回调中同步处理,异步处理(例如 setTimeout 异步执行)无效。
示例代码:
~~~
App({
onPageNotFound(res) {
wx.redirectTo({
url: 'pages/...'
}) // 如果是 tabbar 页面,请使用 wx.switchTab
}
})
~~~
>[warning] 1. 如果开发者没有添加 onPageNotFound 监听,当跳转页面不存在时,将推入微信客户端原生的页面不存在提示页面
> 2. 如果 onPageNotFound 回调中又重定向到另一个不存在的页面,将推入微信客户端原生的页面不存在提示页面,并且不再回调 onPageNotFound
#### 2.2.1.3. 小程序全局数据
小程序的JS脚本是运行在JsCore的线程里,小程序的每个页面各自有一个WebView线程进行渲染,所以小程序切换页面时,小程序逻辑层的JS脚本运行上下文依旧在同一个JsCore线程中。
小程序的App实例是单例的,因此不同页面直接可以通过App实例下的属性来共享数据。
>[info] App构造器可以传递其他参数作为全局属性以达到全局共享数据的目的。
代码清单3-6 小程序全局共享数据
~~~
// app.js
App({
globalData: 'I am global data' // 全局共享数据
})
// 其他页面脚本other.js
var appInstance = getApp()
console.log(appInstance.globalData) // 输出: I am global data
~~~
>[danger] 由于所有页面的脚本逻辑都跑在同一个JsCore线程,页面使用setTimeout或者setInterval的定时器,然后跳转到其他页面时,这些定时器并没有被清除,需要开发者自己在页面离开的时候进行清理。
### 2.2.2 页面
一个小程序可以有很多页面,每个页面承载不同的功能,页面之间可以互相跳转。
#### 2.2.2.1 页面的文件构成和路径
一个页面是分三部分组成:
* 界面:`WXML`文件和`WXSS`文件描述
* 配置:由`JSON`文件进行描述
* 逻辑:`JS`脚本文件负责
>[danger] 一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的。
>[danger] 页面路径需要在小程序代码根目录app.json中的pages字段声明,否则这个页面不会被注册到宿主环境中。
1. **全局配置**
`app.json`文件用来对微信小程序进行全局配置,决定小程序各个页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。[【官方文档详细说明】](https://developers.weixin.qq.com/miniprogram/dev/framework/config.html)
:-: app.json 配置项列表
| 属性 | 类型 | 必填 | 描述 |
|--- | --- |--- | --- |
| pages | String Array | 是 | 设置页面路径 |
| window | Object | 否 | 设置默认页面的窗口表现 |
| tabBar | Object | 否 | 设置底部 tab 的表现 |
| networkTimeout | Object | 否 | 设置网络超时时间 |
| debug | Boolean | 否 | 设置是否开启 debug 模式 |
代码清单3-7 app.json包含了所有配置选项。默认pages字段的第一个页面路径为小程序的首页。
~~~
{
"pages": [
"pages/index/index", // 第一项默认为首页
"pages/logs/index"
],
"window": {
"navigationBarTitleText": "Demo"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页"
}, {
"pagePath": "pages/logs/logs",
"text": "日志"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile":10000
},
"debug": true
}
~~~
2. **页面配置**
每一个小程序页面使用`.json`文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。
页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键,如:
| 属性 | 类型 | 默认值 | 描述 |
| --- | --- | --- | --- |
| navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色,如"#000000" |
| navigationBarTextStyle | String | white | 导航栏标题颜色,仅支持 black/white |
| navigationBarTitleText | String | 导航栏标题文字内容 |
| backgroundColor | HexColor | #ffffff | 窗口的背景色 |
| backgroundTextStyle | String| dark | 下拉 loading 的样式,仅支持 dark/light |
| enablePullDownRefresh | Boolean | false | 是否开启下拉刷新,详见页面相关事件处理函数。 |
| disableScroll | Boolean | false | 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项|
| onReachBottomDistance | Number | 50 | 页面上拉触底事件触发时距页面底部距离,单位为px |
例如:
~~~
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
~~~
#### 2.2.2.2 页面构造器--`Page()`
宿主环境提供了` Page()` 构造器用来注册一个小程序页面,Page()在页面脚本`*.js`中调用,Page() 的调用方式如代码清单3-8所示。
Page构造器(`Page()`)接受一个Object参数,指定页面的初始数据、生命周期函数、事件处理函数等。参数说明如表3-4所示。
>[warning] Object 内容在页面加载时会进行一次深拷贝,需考虑数据大小对页面加载的开销
代码清单3-8 Page构造器
~~~
Page({
data: {
text: "This is page data."
},
onLoad: function(options) {
// Do some initialize when page load.
},
onReady: function() {
// Do something when page ready.
},
onShow: function() {
// Do something when page show.
},
onHide: function() {
// Do something when page hide.
},
onUnload: function() {
// Do something when page close.
},
onPullDownRefresh: function() {
// Do something when pull down.
},
onReachBottom: function() {
// Do something when page reach bottom.
},
onShareAppMessage: function () {
// return custom share data when user share.
},
onPageScroll: function() {
// Do something when page scroll
},
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
},
// Event handler.
viewTap: function() {
this.setData({
text: 'Set some data for updating view.'
}, function() {
// this is setData callback
})
},
customData: {
hi: 'MINA'
}
})
~~~
:-: 表3-4 Page构造器的参数
| 参数属性 | 类型 | 描述 |
| --- |--- | --- |
| data | Object | 页面的初始数据 |
| onLoad | Function | 生命周期函数--监听页面加载,触发时机早于onShow和onReady |
| onReady | Function | 生命周期函数--监听页面初次渲染完成 |
| onShow | Function | 生命周期函数--监听页面显示,触发事件早于onReady |
| onHide | Function | 生命周期函数--监听页面隐藏 |
| onUnload | Function | 生命周期函数--监听页面卸载 |
| onPullDownRefresh | Function | 页面相关事件处理函数--监听用户下拉动作 |
| onReachBottom | Function | 页面上拉触底事件的处理函数 |
| onShareAppMessage | Function | 用户点击右上角转发 |
| onPageScroll | Function | 页面滚动触发事件的处理函数 |
| onTabItemTap | Function | 当前是 tab 页时,点击 tab 时触发 |
| 其他 | Any | 可以添加任意的函数或数据,在Page实例的其他函数中用 this 可以访问 |
上表中的Page构造器`page()`的参数可分为4部分
* ***初始化数据***
`data`属性是当前页面WXML模板中可以用来做数据绑定的初始数据;初始数据将作为页面的第一次渲染使用。
`data`将会以 JSON 的形式由逻辑层传至渲染层,所以其数据必须是可以转成 JSON 的格式:字符串,数字,布尔值,对象,数组。
渲染层可以通过 WXML 对数据进行绑定。
示例代码:
~~~HTML
<!-- *.wxml -->
<view>{{text}}</view>
<view>{{array[0].msg}}</view>
~~~
~~~JavaScript
//*.js
Page({
data: {
text: 'init data',
array: [{msg: '1'}, {msg: '2'}]
}
})
~~~
* ***生命周期函数***
`onLoad / onReady / onShow / onHide /onUnload` 5个回调是Page实例的生命周期函数;
生命周期的触发以及页面的路由方式[【详见】](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html)
* ***事件处理函数***
由用户行为触发的事件回调处理函数:`onPullDownRefresh / onReachBottom / onShareAppMessage / onPageScroll / onTabItemTap` 5个
* ***其他(用户自定义函数/组件事件处理函数)***
Page 中还可以用户自定义函数/组件事件处理函数。在渲染层的组件中加入事件绑定,当事件被触发时,就会执行 Page 中定义的事件处理函数。
示例代码:
~~~HTML
<!-- // *.wxml -->
<view bindtap="viewTap"> click me </view>
~~~
~~~JavaScript
// *.js
Page({
viewTap: function() {
console.log('view tap')
}
})
~~~
#### 2.2.2.3 页面的生命周期和打开参数
:-: ![Page 实例的生命周期图](https://box.kancloud.cn/969257705be6d7c429e0ecb05ff2d9a8_384x602.png)
:-: Page 实例的生命周期图
下述Page实例的5个生命周期函数/方法是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该在其他代码中主动调用Page实例的生命周期函数。
**`onLoad(Object query)`**:页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数(参数名称:query,参数类型:Object)。
使用场景举例:购物商城的小程序,一个商品列表页和商品详情页,点击商品列表页的商品就可以跳转到该商品的详情页。
当然我们不可能为每个商品单独去实现它的详情页。我们只需要实现一个商品详情页的`pages/detail/detail`(代表WXML/WXSS/JS/JSON文件)即可,在列表页打开商品详情页时把商品的id传递过来,详情页通过刚刚说的onLoad回调的参数option就可以拿到商品id,从而绘制出对应的商品,代码如代码清单3-9所示。
代码清单3-9 Page构造器页面的打开参数
~~~JavaScript
// pages/list/list.js
// 列表页使用navigateTo跳转到详情页
wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })
~~~
~~~JavaScript
// pages/detail/detail.js
Page({
onLoad: function(option) {
console.log(option.id)
console.log(option.other)
}
})
~~~
小程序把页面的打开路径定义成页面URL,其组成格式和网页的URL类似,在页面路径后使用英文` ?` 分隔path和query部分。
* query部分:多个参数使用 `&` 进行分隔,参数的名字和值使用 key=value 的形式声明。
在页面Page构造器里onLoad的option可以拿到当前页面的打开参数,其类型是一个Object,其键值对与页面URL上query键值对一一对应。
和网页URL一样,页面URL上的value如果涉及特殊字符(例如:`&`字符、`?`字符、`中文`字符等,详情参考URI的RFC3986说明 ),需要采用UrlEncode后再拼接到页面URL上。
**`onShow()`**:页面显示/切入前台时触发。
**`onReady()`**:页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
>[warning] 对界面内容进行设置的 API 如wx.setNavigationBarTitle,请在onReady之后进行。
**`onHide()`**:页面隐藏/切入后台时触发。 如 `navigateTo` 或底部 `tab` 切换到其他页面,小程序切入后台等。
**`onUnload()`** :页面卸载时触发。如`redirectTo`或`navigateBack`到其他页面时。
* 页面生命周期函数调用过程说明
页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器参数所定义的`onLoad`方法会被调用,onLoad在页面没被销毁之前只会触发1次,在onLoad的回调中,可以获取当前页面所调用的打开参数query。
页面显示之后,Page构造器参数所定义的`onShow`方法会被调用,一般从别的页面返回到当前页面时,当前页的onShow方法都会被调用。
在页面初次渲染完成时,Page构造器参数所定义的`onReady`方法会被调用,onReady在页面没被销毁前只会触发1次,onReady触发时,表示页面已经准备妥当,在逻辑层就可以和视图层进行交互了。
以上三个事件触发的时机是`onLoad`早于` onShow`,`onShow`早于`onReady`。
页面不可见时,Page构造器参数所定义的`onHide`方法会被调用,这种情况会在使用`wx.naviagteTo`切换到其他页面、底部tab切换时触发。
当前页面使用`wx.redirectTo`或`wx.navigateBack`返回到其他页时,当前页面会被微信客户端销毁回收,此时Page构造器参数所定义的`onUnload`方法会被调用。
#### 2.2.2.4 页面的数据
* `data`参数
小程序的页面结构由对应的`*.wxml`文件进行描述,可以通过数据绑定的语法绑定从逻辑层传递过来的数据字段(页面Page构造器的data参数中定义的字段),data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。
* `setData`函数
宿主环境所提供的Page实例的原型中有`setData`函数,我们可以在Page实例下的方法调用`this.setData`把数据传递给渲染层,从而达到更新界面的目的。
由于小程序的渲染层和逻辑层分别在两个线程中运行,所以setData传递数据实际是一个异步的过程,所以setData的第二个参数是一个callback回调,在这次setData对界面渲染完毕后触发。
setData其一般调用格式是 `setData(data, callback)`,其中`data`是由多个`key: value`构成的Object对象。
代码清单3-11 使用setData更新渲染层数据
~~~JavaScript
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在这次setData对界面渲染完毕后触发
})
}
})
~~~
实际在开发的时候,页面的data数据会涉及相当多的字段,只需要把改变的值进行设置即可,宿主环境会自动把新改动的字段合并到渲染层对应的字段中。`data`中的key还可以非常灵活,还可以数据路径的形式给出,例如 `this.setData({"d[0]": 100})`; `this.setData({"d[1].text": 'Goodbye'})`;
>[warning] * 提高小程序的渲染性能的原则:`每次只设置需要改变的最小单位数据`。
> * 直接修改 Page实例的this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
> * 由于`setData`是需要两个线程的一些通信消耗,为了提高性能,每次设置的数据不应超过`1024kB`。
> * 不要把`data`中的任意一项的value设为`undefined`,否则可能会有引起一些不可预料的bug。
代码清单3-12 使用setData更新渲染层数据
~~~JavaScript
// page.js
Page({
data: {
a: 1, b: 2, c: 3,
d: [1, {text: 'Hello'}, 3, 4]
}
onLoad: function(){
// a需要变化时,只需要setData设置a字段即可
this.setData({a : 2})
}
})
~~~
#### 2.2.2.5 页面的用户行为
小程序宿主环境提供了5个和页面相关的用户行为回调:
1. **`onPullDownRefresh()`**:监听用户下拉刷新事件。
* 需要在app.json的window选项中或页面配置中开启`enablePullDownRefresh`。
* 可以通过`wx.startPullDownRefresh`触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。
* 当处理完数据刷新后,`wx.stopPullDownRefresh`可以停止当前页面的下拉刷新。
2. **`onReachBottom()`**:监听用户上拉触底事件。
* 可以在app.json的window选项中或页面配置中设置触发距离`onReachBottomDistance`。
* 在触发距离内滑动期间,本事件只会被触发一次。
3. **`onShareAppMessage(Object)`** :监听用户点击页面内转发按钮(`<button>` 组件 `open-type="share"`)或右上角菜单“转发”按钮的行为,并自定义转发内
容。
>[warning] 只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
Object 参数说明:
| 参数 | 类型 | 说明 | 最低版本 |
| ---| ---| --- | --- |
| from | String | 转发事件来源。`button`:页面内转发按钮;`menu`:右上角转发菜单 | 1.2.4 |
| target | Object | 如果 from 值是 button,则 target 是触发这次转发事件的 button,否则为 undefined | 1.2.4 |
| webViewUrl | String | 页面中包含<web-view>组件时,返回当前<web-view>的url | 1.6.4 |
此事件需要 return 一个 Object,用于自定义转发内容,返回内容如下:
自定义转发内容
| 字段 | 说明 | 默认值 | 最低版本 |
| ---| ---| --- | --- |
| title | 转发标题 | 当前小程序名称 | |
| path | 转发路径 | 当前页面 path ,必须是以 / 开头的完整路径 | |
| imageUrl | 自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径。支持PNG及JPG。显示图片长宽比是 5:4。 | 使用默认截图 |
1.5.0 |
示例代码
~~~JavaScript
Page({
onShareAppMessage: function (res) {
if (res.from === 'button') {
// 来自页面内转发按钮
console.log(res.target)
}
return {
title: '自定义转发标题',
path: '/page/user?id=123'
}
}
})
~~~
4. **`onPageScroll(Object)`**:监听用户滑动页面事件。
Object 参数说明:
| 属性 | 类型 | 说明 |
| ---| ---| --- |
| scrollTop | Number | 页面在垂直方向已滚动的距离(单位px) |
5. **`onTabItemTap(Object)`**:
> 基础库 1.9.0 开始支持,低版本需做兼容处理
点击 tab 时触发
Object 参数说明:
| 参数 | 类型 | 说明 | 最低版本 |
| ---| ---| --- | --- |
| index | String | 被点击tabItem的序号,从0开始 | 1.9.0 |
| pagePath | String | 被点击tabItem的页面路径 | 1.9.0 |
| text | String | 被点击tabItem的按钮文字 | 1.9.0 |
示例代码:
~~~JavaScript
Page({
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
}
})
~~~
* 用户自定义函数/组件事件处理函数
示例代码
~~~HTML
<!--index.wxml-->
<view>{{text}}</view>
<button bindtap="changeText"> Change normal data </button>
<view>{{num}}</view>
<button bindtap="changeNum"> Change normal num </button>
<view>{{array[0].text}}</view>
<button bindtap="changeItemInArray"> Change Array data </button>
<view>{{object.text}}</view>
<button bindtap="changeItemInObject"> Change Object data </button>
<view>{{newField.text}}</view>
<button bindtap="addNewField"> Add new data </button>
~~~
~~~JavaScript
//index.js
Page({
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'
})
}
})
~~~
#### 2.2.2.6 页面路由和跳转
在小程序中所有页面的路由全部由框架进行管理。
1. **页面栈**
一个小程序拥有多个页面,我们可以通过`wx.navigateTo`推入一个新的页面。在首页使用2次`wx.navigateTo`后,页面层级会有三层,我们把这样的一个页面层级称为`页面栈`。
页面栈描述:\[ pageA, pageB, pageC ],其中pageA在最底下,pageC在最顶上,也就是用户所看到的界面。小程序宿主环境限制了这个页面栈的最大层级为10层 ,也就是当页面栈到达10层之后就没有办法再推入新的页面了。
当发生路由切换的时候,页面栈的表现如下:
| 路由方式 | 页面栈表现 |
| --- | --- |
| 初始化 | 新页面入栈 |
| 打开新页面 | 新页面入栈 | |
| 页面重定向 | 当前页面出栈,新页面入栈 |
| 页面返回 | 页面不断出栈,直到目标返回页 |
| Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 |
| 重加载 | 页面全部出栈,只留下新的页面 |
2. **getCurrentPages()**
`getCurrentPages()` 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
>[danger] 不要尝试修改页面栈,会导致路由以及页面状态错误。
3. **路由方式**
对于路由的触发方式以及页面生命周期函数如下:
| 路由方式 | 触发时机 | 路由前页面 | 路由后页面 |
| --- | --- | --- |---|
| 初始化 | 小程序打开的第一个页面 | | onLoad, onShow |
| 打开新页面 | 调用 API `wx.navigateTo` 或使用组件 `<navigator open-type="navigateTo"/>` | onHide | onLoad, onShow |
| 页面重定向 | 调用 API `wx.redirectTo` 或使用组件 `<navigator open-type="redirectTo"/>` | onUnload | onLoad, onShow |
| 页面返回 | 调用 API `wx.navigateBack` 或使用组件`<navigator open-type="navigateBack">`或用户按左上角返回按钮 | onUnload | onShow |
| Tab 切换 | 调用 API `wx.switchTab` 或使用组件 `<navigator open-type="switchTab"/>` 或用户切换 Tab | | 各种情况请参考下表 |
| 重启动 | 调用 API `wx.reLaunch` 或使用组件`<navigator open-type="reLaunch"/>` | onUnload | onLoad, onShow |
Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例):
| 当前页面 | 路由后页面 | 触发的生命周期(按顺序)|
| --- | --- | --- |
| 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() |
4. **和路由相关的API**
* `wx.navigateTo({ url: 'pageD' })`: 可以往当前页面栈多推入一个 pageD,此时页面栈变成 \[ pageA, pageB, pageC, pageD ]。只能打开非TabBar页面。
* `wx.navigateBack()` 可以退出当前页面栈的最顶上页面,此时页面栈变成 \[ pageA, pageB, pageC ]。
* `wx.redirectTo({ url: 'pageE' })` 是替换当前页变成pageE,此时页面栈变成 \[ pageA, pageB, pageE ],当页面栈到达10层没法再新增的时候,往往就是使用redirectTo这个API进行页面跳转。只能打开非TabBar页面。
* `wx.switchTab`。页面栈中使用`wx.switchTab({ url: 'pageF' })`,此时原来的页面栈会被清空(除了已经声明为Tabbar页的pageA外其他页面会被销毁),然后会切到pageF所在的tab页面,页面栈变成 \[ pageF ],此时点击Tab1切回到pageA时,pageA不会再触发onLoad,因为pageA没有被销毁。
* `wx.reLaunch`。我们还可以使用`wx. reLaunch({ url: 'pageH' })` 重启小程序,并且打开pageH,此时页面栈为 \[ pageH ]。
小程序提供了原生的`Tabbar`支持,我们可以在`app.json`声明tabBar字段来定义Tabbar页(注:更多详细参数见Tabbar官方文档 )。
代码清单3-14 app.json定义小程序底部tab
~~~JavaScript
{
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
}
~~~
>[warning] * `wx.navigateTo`, `wx.redirectTo` 只能打开非 tabBar 页面。
> * `wx.switchTab` 只能打开 tabBar 页面。
> * `wx.reLaunch` 可以打开任意页面。
> * 页面底部的 `tabBar` 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
> * 调用页面路由带的参数可以在目标页面的`onLoad`中获取。
5. **路由信息 `Page.prototype.route`**
>基础库 1.2.0 开始支持,低版本需做兼容处理
route 字段可以获取到当前页面的路径。
~~~JavaScript
//index.js
Page({
onShow:function(){
console.log(this.route)
}
})
~~~
- 微信
- 小程序
- 1. 代码组成
- 1.1 JSON配置--'*.json'文件
- 1.2 WXML模板--'*.wxml'文件
- 1.3 WXSS样式--'*.wxss'文件
- 1.4 JavaScript脚本--'*.js'文件
- 2. 客户端运行
- 2.1 逻辑层和渲染层
- 2.1.1 逻辑层--App Service
- 2.1.2 渲染层/视图层--View
- 2.1.3 通信模型
- 2.1.4 数据驱动
- 2.1.5 双线程下的界面渲染
- 2.2 程序与页面
- 2.3 组件
- 2.4 API
- 2.5 事件
- 2.6 兼容
- 3. 应用设计
- 3.1 Flex布局
- 3.2 界面常见的交互反馈
- 3.3 发起HTTPS网络通信--wx.request
- 3.4 微信登录
- 3.5 本地数据缓存
- 3.6 设备能力
- 4. 小程序的协同工作和发布
- 4.1 协同工作
- 4.2 用户体验审视
- 4.3 发布
- 4.4 运营
- 5. 底层框架
- 5.1 双线程模型
- 5.2 组件系统--Exparser框架
- 5.3 原生组件
- 5.4 小程序与客户端通信原理
- 6. 运行和性能优化
- 6.1 启动--代码加载
- 6.2 页面准备
- 6.3 数据通信
- 6.4 视图层渲染
- 6.5 原生组件通信
- 7. 小程序基础库的更新迭代
- 8. 微信开发者工具
- 腾讯云支持
- wafer
- Wafer2 快速开发 Demo - PHP
- WXAPI
- api列表