5.页面跳转与传参
小程序启动后会自动加载缺省页面作为小程序的首页,小程序的缺省页面通过app.json中的"entryPagePath"来设置,若您没有设置"entryPagePath",则将"pages"中的第一个页面作为缺省页面。一个小程序通常由多个页面组成,除了小程序的首页外,开发者需要使用小程序的navigator组件或者是小程序提供的导航函数来实现页面的切换。
5.1页面跳转与页面栈
在小程序中有两种方式从一个页面跳转的另外一个页面,第一种方式是使用navigator组件,navigator相当于HTML中的a标签。例如当前页面是home页面,若要从首页home页面跳转到page_a页面,您可以在home页面添加以下WXML代码:
<!--home.wxml-->
<navigator url="/pages/page_a">
<button>显示page_a页面</button>
</navigator>
当用户点击【显示page_a页面】按钮时小程序会加载/pages/page_a页面,并把/pages/page_a页面作为当前页面。
页面跳转的第二种方式是使用导航函数,上面的页面跳转也可以这样实现:首先为WXML文件中的按钮绑定事件,然后在事件处理函数中调用wx.navigateTo函数进行页面跳转,以下是页面跳转的示例代码:
<!--home.wxml-->
<view bindtap="onShowPageA">显示page_a页面</view>
//page_a.js
Page({
onShowPageA:function(e){
wx.navigateTo({
url: '/pages/page_a',
})
}
})
小程序跳转到/pages/page_a页面后并不会立即销毁home页面,小程序打开一个页面时会把页面对象添加到一个称为“页面栈”的数据结构中,在小程序中使用函数:getCurrentPages() 可以获取页面栈中的数据。getCurrentPages()返回结果是一个数组,第一个元素为首页,最后一个元素为当前页面。在上面的例子中,当页面从/pages/home通过wx.navigateTo跳转到/pages/page_a后我们在page_a.js中输出页面栈的内容:
//pages/page_a.js
Page({
onLoad: function (options) {
//获取页面栈
let pages = getCurrentPages();
console.log(pages);
}
})
console.log(pages)的输出结果为:
[vt, vt]
0: vt {route: "pages/home", __displayReporter: e, onLoad: ƒ, …}
1: vt {route: "pages/page_a", __displayReporter: e, onLoad: ƒ, …}
length: 2
…
小程序的这种机制使得小程序编程更像CS编程模式,有了“页面栈”,小程序能够保持页面的状态,当调用wx.navigateBack返回时,原页面能够重新显示,并且页面的数据也能得到够保持。
页面栈中的页面不会随着wx.navigateTo的调用无限制增加,目前小程序宿主环境限制了这个页面栈的最大层级为10层,也就是当页面栈到达10层之后就没有办法再增加页面栈的长度了,这时候调用wx.navigateTo会关闭当前页面,然后再跳转到目标页面,这样页面栈会保持10层的最大长度。
除了wx.navigateTo,小程序还提供了其他几个的页面跳转函数,这些跳转函数应用于不同的跳转场景,有些只能用于特定类型的页面,有的则在跳转前会关闭当前页或关闭全部页面栈中的页面。
- wx.navigateTo
<< 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar页面。使用 wx.navigateBack 可以返回到原页面。 - wx.redirectTo
<< 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar页面。 - wx.switchTab
<< 该接口跳转到tabBar页面,并关闭其他所有非tabBar页面。wx.switchTab函数的url路径必须是在app.json的tabBar配置字段中定义的路径,并且路径后不能带参数。 - wx.navigateBack
<< 该接口关闭当前页面,返回上一级页面或更上级的页面。delta参数指定了页面栈的深度,即返回到第几个页面,默认为1,即返回上一次打开的页面。
示例代码:
wx.navigateBack({
delta: 1
})
- wx.reLaunch
该接口关闭所有页面,打开新页面。该接口可以跳转到非 tabBar页面, 也可以跳转到tabBar页面。
navigator组件
navigator组件根据open-type属性不同,分别对应于5个小程序导航API:
- open-type为navigate,相当于wx.navigateTo
- open-type为redirect,相当于wx.redirect
- open-type为switchTab,相当于wx.switchTab
- open-type为reLaunch,相当于wx.reLaunch
- open-type为navigateBack,相当于wx.navigateBack
示例代码
<view style="padding:10rpx">
<navigator url="/pages/page_a" open-type="navigate">
<button type="default">新窗口打开</button>
</navigator>
<navigator url="/pages/page_a" open-type="redirect">
<button type="default">在当前页打开</button>
</navigator>
<navigator url="/pages/page_tab" open-type="switchTab">
<button type="default">切换tab</button>
</navigator>
<navigator url="/pages/page_a" open-type="reLaunch">
<button type="default">重启打开</button>
</navigator>
<navigator delta="1" open-type="navigateBack">
<button type="default">返回页面</button>
</navigator>
</view>
关于导航API的几个补充点:
- wx.navigateTo和wx.redirectTo只能打开非TabBar页面,wx.switchTab只能打开Tabbar页面,wx.reLaunch可以打开任意页面
- 小程序宿主环境限制了这个页面栈的最大层级为10层,也就是当页面栈到达10层之后就没有办法再推入新的页面了,这时候调用wx.navigateTo会使用redirectTo这个API进行页面跳转。
5.2页面URL传参
页面之间互相跳转时有些情况下需要传递参数,比如我们小程序有一个商品列表页和商品详情页,点击商品列表页中的商品跳转到商品的详情页。为了能够在详情页面中显示用户点击的商品,需要在跳转到详情页面时把商品的id传递过来。
小程序把页面的打开路径定义成页面URL,其组成格式和网页的URL类似,在页面路径后使用英文?符号将分隔URL为path和query部分,query部分的多个参数使用&符号进行分隔,参数的名字和值使用key=value的形式声明。因此可以使用页面URL来进行参数的传递。下面的代码是一个通过页面URL传递参数的列子:
//列表页使用navigateTo跳转到详情页
onShowDetail:function(e){
wx.navigateTo({
url: '/pages/detail?good_id=1',
})
}
navigator组件也可以通过页面URL传递参数,若使用navigator组件进行页面跳转,请在WXML文件中加入以下代码:
<navigator url="/pages/detail?good_id=1">显示详情页</navigator>
目标页面在onLoad事件处理函数中获取传递过来的参数,onLoad函数的参数是一个Object,其键值对与页面URL上query键值对一一对应。和网页URL一样,页面URL上的value如果涉及特殊字符,需要采用UrlEncode编码后再拼接到页面URL上。
//pages/detail.js
Page({
onLoad: function (options) {
var good_id = options.good_id;
console.log(good_id);
}
})
5.3全局变量传参
页面URL传参的方式不能用于TabBar页面的参数传递,这是因为跳转到TabBar页面时路径后不能带参数。另外有些情况下也需要在没有跳转关系的两个页面之间进行参数的传递,这种情况下也不能使用页面URL传递参数。这种情况下可以使用小程序的全局变量进行传参,或者是使用本地数据缓存来传递参数,本节介绍使用全局变量来传递参数。
全局变量的定义
小程序全局变量 在 app.js 中定义。例如:
App({
globalData: {
login:false,
good_id:0
}
})
全局变量的使用
在其他页面中可以读取以及修改globalData中的数据。不论是读取还是修改,首先需要在应用的页面js文件中,通过getApp()获取app实例,然后通过app实例来访问globalData对象。例如:在good_list页面中设置当前选择的商品的ID:
//pages/good_list.js
Page({
//点击商品,显示商品详情
onSpInfo: function (e) {
var spid = e.currentTarget.dataset.spid;
var app = getApp();
app.globalData.good_id = spid;
wx.navigateTo({
url: '/pages/detail',
})
}
})
在detail页面中,也需要通过getApp()获取app实例,然后通过app实例来访问globalData对象:
//pages/detail.js
Page({
onLoad: function (options) {
var app = getApp();
var good_id = app.globalData.good_id;
app.globalData.good_id = 0;
console.log(good_id);
}
})
5.4本地缓存传参
小程序的全局变量存储在内存中,当小程序退出时会跟着销毁,若希望页面之间传递的参数不受小程序退出的影响,就要使用小程序本地缓存技术。
本地缓存是指微信客户端为每个小程序分配的一块本地存储空间,目前一个小程序的本地缓存上限为10MB。本地缓存有非常多的用途,我们可以利用本地缓存来存储用户状态数据,在用户关闭小程序后重新打开时可以恢复之前的状态。我们还可以利用本地缓存存储一些服务端的非实时数据,在特定的场景下可以提高页面的渲染速度,减少用户的等待时间。
小程序提供了读写本地缓存的接口,通过wx.getStorage和wx.getStorageSync读取本地缓存,通过wx.setStorage和wx.setStorageSync写数据到缓存,其中Sync后缀的接口表示是同步接口。
写本地缓存
wx.setStorage和wx.setStorageSync将数据存储在本地缓存中指定的 key 中。若这个key已经存在则会覆盖掉原来的内容。除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用。单个key允许存储的最大数据长度为 1MB,所有数据存储上限为10MB。wx.setStorage/wx.setStorageSync详细参数如下:
- key:本地缓存中指定的 key
- data:需要存储的内容
- success:异步接口调用成功的回调函数
- fail:异步接口调用失败的回调函数
- complete:异步接口调用结束的回调函数(调用成功、失败都会执行)
代码示例:写数据到本地缓存
wx.setStorage({
key:"good_id",
data:"6"
success: function() {},
fail: function() {}
})
//同步接口写入
wx.setStorageSync(good, '6')
从本地缓存读数据
wx.getStorage/wx.getStorageSync详细参数:
- key:本地缓存中指定的 key
- success:异步接口调用成功的回调函数,回调参数格式: {data: key对应的内容}
- fail:异步接口调用失败的回调函数
- complete:异步接口调用结束的回调函数(调用成功、失败都会执行)
代码示例:读取本地缓存的数据
wx.getStorage({
key: 'good_id',
success: function(res) {
var value = res.data
},
fail: function() {}
})
//同步接口读取
var value = wx.getStorageSync('good_id')
5.5页面栈传参
有些情况下数据需要反向传递,这种情况也比较常见,例如用户在提交商品购买订单的操作中,在设置收货地址时需要跳转到地址选择页面(address), 如图5-1所示。在address页面可以添加地址,也可以直接选择一个已有地址,然后返回到订单提交order页面。要求返回order页面时能够把选择的地址数据发送给order页面。
图 5-1 选择收货地址
在网页编程中,类似的场景通常使用“网页弹框”来实现。“网页弹框”是使用页面元素(通常为div)来模拟模态对话框,通过设置div的display样式来控制div的显示与隐藏,其本质上来说是将两个页面的功能在一个页面中实现。
在小程序中也依然可以使用类似的机制来实现这个功能,不过有了页面栈的机制,在小程序中您可以分别实现order页面和address页面,并利用页面栈的机制在address页面页面中获取order页面,然后将数据发送给order页面。以下是实现的步骤以及代码:
首先实现order页面,并实现order页面到address页面的跳转:
//pages/orders.js
Page({
data: {
addr :""
},
//显示收货地址选择页面
onAddressSel(e) {
wx.navigateTo({
url: '/pages/address',
})
},
//设置并刷新收货地址
setAddress:function(addr){
console.log(addr);
this.setData({
addr:addr
});
},
})
代码中的onAddressSel事件函数用于跳转到address页面,成员函数setAddress用于在address页面设置order页面的数据:addr,并刷新页面显示。
接下来实现address页面,在address.js中调用getCurrentPages() 函数获取上一个页面的实例,并调用order页面的成员函数setAddress修改addr的值:
//pages/address.js
function loadAddrs(page) {
var items = [];
items.push("广东省深圳市福田区测试地址1");
items.push("广东省深圳市福田区测试地址2");
items.push("广东省深圳市福田区测试地址2");
page.setData({ addrs: items });
}
Page({
data: {
addrs: []
},
onLoad: function () {
loadAddrs(this);
},
//选择一个地址
selAddress: function (e) {
console.log(e);
var index = e.currentTarget.dataset.index;
var addr = this.data.addrs[index];
var pages = getCurrentPages();
var prevPage = pages[pages.length - 2];
prevPage.setAddress(addr);
wx.navigateBack({});
}
})
getCurrentPages() 函数用于获取页面栈的实例。页面栈中按照页面的路由顺序存放着相应的Page对象,第一个元素为首页,最后一个元素为当前页面。我们可以按照下面的代码来获取上一个页面的对象:
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
var prevPage = pages[pages.length - 2]; //上一个页面
获得了上一个页面对象后就可以通过这个页面对象来修改页面的数据。可以直接调用上一个页面的setData() 方法,把数据存到上一个页面中:
prevPage.setData({
addr:addr
})
需要注意的是页面跳转必须使用wx.navigateTo(),不能使用wx.redirectTo,wx.redirectTo会关闭当前页面,导致跳转的目标页面无法获取上一页页面的实例。