问题背景
小程序存在10层爆栈问题,在开发中因为流程过长,且存在其他页面跳转当前流程的问题,如何确保跳转不超过10层?
解决思路
将当前完整流程做一个单页面应用,每一个步骤作为一个组件,控制其显示隐藏来实现。
需要解决的问题
1、共用一个页面需要自定义导航头,各手机兼容定位;
2、如何控制页面的前进和后退,保证页面顺序正常;
3、一个页面内,如何使页面重复渲染;
4、返回上一页,如何保留之前选中的数据;
5、父子组件传值问题;
6、部分方法在子组件内如何使用问题(如触底监听等)
…
内容实现
初始页面布局
layout.wxml(主页面模版)
<!-- 顶部导航 -->
<nav></nav>
<!-- 内容 -->
<view class="content">
<!-- 子组件 -->
<child1></child1>
<child2></child2>
</view>
<!-- 底部按钮 -->
<bottom-btn></bottom-btn>
layout.json
{
"usingComponents": {
"nav": "../../components/navigation/navigation",
"bottom-btn": "../../components/bottomBtn/bottomBtn",
"child1": "../../components/child1/index",
"child2": "../../components/child2/index",
},
"navigationStyle": "custom", //开启自定义导航头
"navigationBarTitleText": ""
}
自定义导航头
navigation.wxml
<!-- 控制导航距顶高度 -->
<cover-view class="nav" style="height:{{navheight}}px">
<cover-view class="content" scroll-top="0">
<!-- 回退按钮处理 -->
<cover-view class="left" bindtap="bindNav">
<cover-image class="ico" src="../../resources/arrow-left.png" mode="widthFix">
</cover-image>
</cover-view>
<!-- 动态修改标题 -->
<cover-view class="title" wx:if="{{title}}">
{{title}}
</cover-view>
</cover-view>
</cover-view>
navigation.js
const app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
title: String //外部传入的标题
},
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
this.isSupportNav()
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
}
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached: function() {
// 在组件实例进入页面节点树时执行
this.isSupportNav()
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
},
/**
* 组件的初始数据
*/
data: {
statusBarHeight: 0,
navheight: 0
},
/**
* 组件的方法列表
*/
methods: {
// 回退按钮控制
bindNav: function(){
this.triggerEvent('nav', {})
},
/**
* 是否支持自定义导航,计算距顶高度
*/
isSupportNav: function(){
const res = wx.getSystemInfoSync()
// 判断是否是安卓操作系统 (标题栏苹果为44px,安卓为48px)
let titleH;
if(res && res['system']){
if(res['system'].indexOf('Android')>0){
titleH = 48
}else{
titleH = 44
}
const navheight = titleH + res['statusBarHeight']
this.setData({
navheight
})
//将高度传入主页面,确定页面内容距顶部高度
this.triggerEvent('nh', navheight)
}
}
}
})
控制页面显隐
参数说明
wx:if 控制页面初次加载
hidden 保留页面会退数据
this.onShow() 促使页面重新渲染
pageStack:[] 页面栈
pageCtrl 页面层级
父页面
layout.wxml
<!-- 顶部导航 -->
<nav title="{{title}}" bind:nav="bindNav" bind:nh="getTopHeight"></nav>
<!-- 内容 -->
<view class="content">
<!-- 子组件 -->
<child1 wx:if="{{pageCtrl>=1}}" hiddenFlag="{{pageStack.length==1}}" bind:handleLogic="handleLogic" bind:setPageTitle="setPageTitle" bind:handleLogic="handleLogic"></child1>
<child2 wx:if="{{pageCtrl>=2}}" hiddenFlag="{{pageStack.length==2}}" bind:handleLogic="handleLogic" bind:setPageTitle="setPageTitle"></child2>
</view>
<!-- 底部按钮 -->
<bottom-btn></bottom-btn>
layout.js
Page({
/**
* 页面的初始数据
*/
data: {
pageStack: [{}], // 页面栈
pageCtrl: 1
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {},
onShow: function(){
if(typeof this.data.pageStack == 'number'){
const len = this.data.pageStack
const arr = []
for(let i = 0; i < len; i++){
arr.push({})
}
this.setData({
pageStack: arr
})
}
},
/**
* 自定义导航,返回上一页
*/
bindNav: function(){
if(this.data.pageStack.length>1){
const len = this.data.pageStack.length - 1
const pageCtrl = this.data.pageCtrl - 1
this.setData({
pageStack: len,
pageCtrl
})
this.onShow()
return;
}
wx.navigateBack({
delta: 1
})
},
/**
* 修改顶部标题
*/
setPageTitle: function(e){
this.setData({
title: e.detail
})
},
/**
* 自定义底部按钮跳转,页面栈加一
*/
handleLogic: function(){
const pageCtrl = this.data.pageCtrl + 1
this.setData({
pageStack: this.data.pageStack.push({}),
pageCtrl
})
this.onShow()
},
// 获取导航头传过来的高度
getTopHeight: function(e){
this.setData({
navheight: e.detail
})
}
})
子页面
child1.wxml
<view class="child1" hidden="{{!hiddenFlag}}">
<view class="content">子页面内容</view>
<!-- 触发跳转下一页按钮 -->
<bottom-btn bindtap="nextPage"></bottom-btn>
</view>
child1.js
Component({
/**
* 组件的属性列表
*/
properties: {
hiddenFlag: {
type: Boolean
}
},
/**
* 组件的初始数据
*/
data: {
},
attached: function(){
this.getInitInfo()
},
/**
* 组件的方法列表
*/
methods: {
// 获取页面初始配置信息
getInitInfo(){
},
// 跳转相关逻辑
nextPage(){
this.triggerEvent('handleLogic');
}
}
})
左滑回退
layout.html
<view class="content" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd">
</view>
layout.js
//获取应用实例
const app = getApp();
var startX, endX;
var moveFlag = true;// 判断执行滑动事件
Page({
touchStart: function (e) {
startX = e.touches[0].pageX; // 获取触摸时的原点
moveFlag = true;
},
// 触摸移动事件
touchMove: function (e) {
endX = e.touches[0].pageX; // 获取触摸时的原点
if (moveFlag) {
if (endX - startX > 50) {
// 右滑逻辑
moveFlag = false;
}
if (startX - endX > 50) {
// 左滑逻辑
moveFlag = false;
}
}
},
// 触摸结束事件
touchEnd: function (e) {
moveFlag = true; // 回复滑动事件
}
})
子组件上拉加载问题(父组件调用子组件方法)
小程序主页面的onReachBottom方法可以监听到页面是否到底部,然后调用接口可实现上拉加载的功能。但是该方法在组件中无效,接口调用又在子组件中,所以需要在父页面的onReachBottom方法中调用子页面的方法。
组件调用定义id,通过id调用子组件方法
layout.html
<child id="search"></child>
layout.js
// 上拉加载
onReachBottom: function () {
if(this.data.pageStack.length===3){
this.selectComponent('#search').getList()//后台获取新数据并追加渲染
}
}
存在问题
1、ios手机右滑自动回退上一页,目前无法禁止