跨平台应用开发进阶(二十一) :uni-app 路由传参参数丢失问题解决方案探究

一、前言

uni-app项目开发过程中,页面间跳转的同时进行参数传递是再普通不过的应用场景。但是对于超长文本参数的传递场景,很多初级开发者并不会留意其中存在的坑。

本人在通过uni.navigateTo进行路由跳转时,竟遇到了超长文本作为路由传递参数,目的路由页面接收参数时参数丢失,从而报 "SyntaxError: Unexpected end of JSON input"的错误。

首先,定位报错语句:

javascript 复制代码
this.detailData = JSON.parse(options.detailData);

在执行以上JSON解析语句时,出现以下错误信息:

java 复制代码
[Vue warn]: Error in onLoad hook: "SyntaxError: Unexpected end of JSON input"

报错原因:很明显,以上报错原因是由于待转换为JSON字符串的对象不满足JSON格式。

A页面(原页面):

javascript 复制代码
uni.navigateTo({
    url: '/pages/nextPage?detailData=' + JSON.stringify(item.detailData)
});

B页面(目标页面):

javascript 复制代码
onLoad(options) {
	console.log('options.detailData', options.detailData)
	this.detailData = JSON.parse(options.detailData);
},

通过console控制台打印日志发现,A页面由于传输参数过长,导致B页面接收的时候出现了参数丢失的现象。

注意⚠️:url有长度限制,太长的字符串会传递失败⚠️,可改用窗体通信 postMessage、全局变量Vuex,另外参数中出现空格等特殊字符时需要对参数进行编码,如下为使用encodeURIComponent对参数进行编码的示例。

上面所说的url有长度限制,太长的字符串具体指多长呢?网上有人说是几百K左右,自己可以做个实验验证下。

二、解决措施

可使用窗体通信 postMessage、页面通信uni.$emit(eventName,OBJECT)、全局变量globalData或者Vuex

2.1 应用全局变量 globalData

第一步:在App.vue中配置全局变量:

javascript 复制代码
<script>  
    export default {  
        globalData: {  
            text: 'text'  
        }
    }  
</script>

第二步:在页面中写全局变量:

javascript 复制代码
getApp().globalData.text = 'test'

注意⚠️:在onLaunch获取全局变量时,由于getApp对象还未获取,暂时可以使用this.$scope.globalData获取globalData

当在onLoad获取全局变量的时候,可以通过getApp().globalData.text直接获取。

2.2 应用全局变量 Vuex

对于熟悉Vue的童鞋来说,状态管理工具Vuex应该特别熟悉了。具体用法及注意事项不在此赘述,详参博文《Vue进阶(五):与 Vuex 的第一次接触》。

具体实现如下: 首先定义一个store.js公共文件

javascript 复制代码
// 存储数据的对象,可以将需要存储的数据在这个state中定义
const state = {
  // 当前登陆的用户名
  username: ''
}
const mutations = {
  // 提供一个方法,为state中的username赋值
  // 这些方法有一个默认的参数,这个参数就是当前store中的State
  setUserName (state, username) {
    //存入一个值
    state.username = username
  },
  getUserName (state) {
    //输出一个值
    return state.username
  }
}
//使用的时候---> 通过commit调用mutations中定义的函数,这个函数就是操作state中定义的成员的函数
// this.$store.commit('setUserName', res.data.username(请求返回的值))
const actions = {
  setUserNameAction: ({commit}, username) => {
    commit('setUserName', username)
  },
  getUserNameAction: ({commit}) => {
    commit('getUserName')
  }
}
// 通过action来触发mutations中的函数,这种触发方式是异步方式
//存入  this.$store.dispatch('setUserNameAction', res.data.username + 'aa')
//取出  this.currentUserName = this.$store.dispatch('getUserNameAction')

// Getters是从 store 中的 state 中派生出一些状态,即当出现多处需要导入某个状态时,结果不是很理想,所以getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
const getters = {
  getUserName: (state) => {
    return localStorage.getItem('myname')
  }
}
//使用的时候,直接使用
// <span class="welcome">你好:{{$store.getters.getUserName}}</span>

A页面(原页面):

javascript 复制代码
this.$store.commit('setUserName', pageParams);

B页面(目标页面):

javascript 复制代码
this.$store.state.username

或者通过getters方式获取。

javascript 复制代码
this.$store.getters.userName

2.3 应用窗体通信 postMessage

javascript 复制代码
window.postMessage(msg,targetOrigin)

注意⚠️postMessage要通过window对象调用!因为这里的window不只是当前window!大部分使用postMessage的时候,都不是本页面的window,而是其他网页的window!如:

  • iframecontentWindow
  • 通过window.open方法打开新窗口的window
  • window.opener

如果你使用postMessage时没有带window,就是用的本页面的window来调用它。

A页面(原页面):

javascript 复制代码
window.parent.postMessage({ msg:"xxx"},'*');

B页面(目标页面):

javascript 复制代码
// 页面销毁前,务必去除监听器,否则会造成资源泄露!
beforeDestory () {
	window.removeEventListener('message', this.listenerFun)
}

mounted() {
	window.addEventListener('message',this.listenerFun)
}

methods: {
  listenerFun (e) {
	console.log(e.data);
    if(e.data.msg==='xxx'){
        // 业务处理逻辑
    }
  }
}

2.4 应用页面通信uni.$emit(eventName,OBJECT)

uni.$emit(eventName,OBJECT)uni-app框架自带的页面间通信方法。

在A页面通过uni.$emit触发全局的自定义事件,

javascript 复制代码
uni.$emit('connectStatusChange',connectEnable);

在B页面中,通过uni.$on监听到全局的自定义事件。

javascript 复制代码
uni.$on('add', this.add)

注意⚠️:使用时,注意及时销毁事件监听,比如,页面 onLoad 里边 uni.$on 注册监听,onUnload 里边 uni.$off 移除,或者一次性的事件,直接使用 uni.$once 监听。

其实,关于全局存储的方法还有很多,例如sessionStorage,localStorageuni.setStorage。这里就不再一一详述了,可详参博文《跨平台应用开发进阶(十) :uni-app 实现数据存储、获取和删除》。

三、延伸阅读 uni-app实现内容文本置顶操作方案探究

3.1 场景再现

uni-app项目开发过程中,需要实现文本内容置底操作(例如版权声明、免责声明等文本内容)。

刚开始自己通过position: fixed;方式实现,对于一屏页面内容,此种样式可以实现文本内容固定于页面底部。但是,当页面内容长于屏幕高度时,就会出现文本内容悬浮于正文内容并出现遮挡的问题。

3.2 解决措施

正确的解决措施如下:

html 复制代码
<template>
	<view class="containerClass">
		<view class="contentClass">
		...
		</view>
		<view class="footerClass">
			...
		</view>
	</view>
</template>

CSS部分如下:

css 复制代码
.containClass {
	min-height: 100vh;
}
.contentClass {
	padding: 0 32rpx;
	min-height: 85vh;
}
.footerClass {
	padding: 0 32rpx;
	height: 15vh;
}

四、拓展阅读

相关推荐
EricWang135810 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning10 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人20 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
超雄代码狂42 分钟前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css
嚣张农民1 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
周亚鑫1 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
Justinc.2 小时前
CSS3新增边框属性(五)
前端·css·css3
neter.asia2 小时前
vue中如何关闭eslint检测?
前端·javascript·vue.js