写点什么

InfoQ 极客传媒 15 周年庆征文|uni-app 路由传参参数丢失问题解决方案探究

  • 2022 年 6 月 16 日
  • 本文字数:2882 字

    阅读完需:约 9 分钟

InfoQ 极客传媒 15 周年庆征文|uni-app 路由传参参数丢失问题解决方案探究

一、前言

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


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


首先,定位报错语句:


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


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


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


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


A 页面(原页面):


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


B 页面(目标页面):


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中配置全局变量:


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


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


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


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


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

2.2 应用全局变量 Vuex

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


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


// 存储数据的对象,可以将需要存储的数据在这个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 页面(原页面):


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


B 页面(目标页面):


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


或者通过getters方式获取。


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

2.3 应用窗体通信 postMessage

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


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


  • iframecontentWindow

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

  • window.opener


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


A 页面(原页面):


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


B 页面(目标页面):


// 页面销毁前,务必去除监听器,否则会造成资源泄露!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触发全局的自定义事件,


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


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


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 解决措施

正确的解决措施如下:


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


CSS部分如下:


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

四、拓展阅读

发布于: 刚刚阅读数: 5
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
InfoQ 极客传媒 15 周年庆征文|uni-app 路由传参参数丢失问题解决方案探究_前端_No Silver Bullet_InfoQ写作社区