一、前言
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
!如:
iframe
的contentWindow
;- 通过
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
,localStorage
及uni.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;
}