注册

uni-app跨端开发之疑难杂症

前言

今年,公司决定解决各个团队移动端开发的混战局面,由架构部出一套移动端框架,规范化开发标准。经过一段时间的调研,考虑到跨端以及公司主要技术栈为vue,最终选择了uni-app作为移动端框架,在大家都“很忙”的情况下,我成为了移动端框架的主要开发。以前就总听同事说,uni-app有很多坑,我对其也只是有些许了解,这回的全身心投入,才知道一入深坑愁似海

这段时间也做了一些成效,头大如斗的路由拦截、必不可少的http请求封装、提高成效的组件库、仿照微信的oAuth 2.0登录、复杂逻辑的离线存储、用户需要的增量更新包

有成效也踩了一些坑,百思不得解的console.log、烦到吐血的网络调试、爬坑许久的APP与h5通讯、性能极差的微信小程序端uni.canvasToTempFilePath

今天就要聊聊一些疑难杂症,有些忘记了,有些还没碰到,后续持续更新吧!

百思不得解的console.log

移动端框架是采用npm包的方式提供给业务部门使用,其中包含oAuth2.0登录方式,这其中涉及到了h5通过scheme协议唤醒app并且带回code等参数,相应的参数会存放在plus.runtime.arguments,其他情况下,plus.runtime.arguments的值为空。在给同事排查问题时我就简单操作,在node_modules对应的npm包里面写了不是很严谨的如下代码:

const args = plus.runtime.arguments;
// 这个是业务部门出错时,我添加的调试代码
console.log('>>>>>>'args)
if (args) {
 const isLogout = args.includes('logout');
 if (isLogout) {
   await this.handleSession();
else {
   await this.handleAuthorization(args);
}
}

我测试是正常的,args是空值,所以是不会执行if内的逻辑的,但是他这边会执行if内的逻辑的,初步判断args由于某个原因导致存在值了,为了简单明了的查看输出内容,然后我就写了毁一生的console.log('>>>>>>', args),这行调试代码的输出内容如下,我一直以为args是空值,但是判断依旧为true,有点颠覆了我的人生观,后来灵机一动,删掉了第一个修饰参数,发现args原来是有值的,经过排查,是因为添加了微信小程序打开指定页面,导致记录当前页面数据。

f8d3ebded2b3ef06508652f5275142e9.png

烦到吐血的网络调试

网络调试对于我们的日常开发是很重要的,有助于快速判断资源请求问题,但uni-app在这方面有很大的缺陷,在讨论这个问题时,先来看一下uni-app的真机调试方式。

终端调试工具

当项目运行时,点击终端上的调试按钮,会弹出一个调试界面。

4a6c1ce491fb96554b523a48a9a64861.png

从调试面板中,可以看到仅有ConsoleElementsSources三个选项,期待许久的Network并没有出现,这种调试方式没办法实现网络请求调试。

3a6ce13e15484c62f8f29e2f46dae45d.png

webview调试控制台

点击工具栏的 运行 -> 运行到手机或模拟器 -> 显示webview调试控制台 会出现一个跟谷歌浏览器一样的调试界面,虽然这里有Network,但是很可惜,这个功能存在问题,没办法监听到网络请求。

cfcf5063252c0c14b834e73edfe73571.png

Fiddler 抓取网络请求

在走投无路之下,只能另辟蹊径,借助工具,抓取真机的网络请求,接下来阐述一下怎么使用Fiddler抓取真机的网络请求,配置完需要重启才生效。

下载Fiddler

这是一个免费工具,自行在网络上下载即可。

Fiddler 基础配置

点击工具栏的tools,选择options就会弹出一个配置界面

b2610ad088dce9c0a967f0266448122a.png

0b7dcb3e34c6c0d2ff55eb657afc7089.png

HTTPS 配置

选择HTTPS选项,勾选选矿中的Capture HTTPS CONNECTsDecrypt HTTPs trfficIgnore server certificate errors

5da7ad2ab31dbcb0bcb8939b890a6952.png

Connections 配置

这边配置的端口号后面配置代理的时候需要使用到。

75d7d9888efe87e6ed1a4293febca748.png

手机配置代理

注意需要和电脑连接同一网络,点击进入手机WIFI详情界面,有个代理,选择手动模式,输入电脑的IP地址和Fiddler的监听端口,即可拦截到真机的所有网络请求,包含我们app对应的网络请求。

dc792a8c752a444161f12f915569d655.png

过滤

这边可以选择过滤对应的ip或域名,多个的话通过分号隔开即可。

11065384e257f90c50be4fbd16640e4b.png

爬坑许久的APP与h5通讯

谈论这个问题时,先描述一下uni-app实现的app怎么和h5通讯

app端

对于app端的通讯,.vue.nvue有两点区别,1. 获取webView实例不一致,2. 监听方法不一致。app向h5传递数据时,需要借助webview.evalJS执行h5的全局方法,而h5向app传递参数时,类似于h5发送postMessage,可以在webview的message/onPostMessage监听函数获取数据。

vue

获取webView示例

webView实例的获取,对于vue文件不是特别友好,需要借助于this.$scope.$getAppWebview(),如果是在组件中需要使用this.$parent.$scope.$getAppWebview(),添加延时的原因是,h5页面可能未加载完成,无法获取到对应的全局函数,会提示xxx函数undefined;

<template>
   <web-view src="http://www.juejin.com"></web-view>
</template>
<script>
   export default {
       onReady() {
           const currentWebview = this.$scope.$getAppWebview();
           const account = '清欢bx'
           setTimeout(() => {
               const webView = currentWebview.children()[0];
               webView.evalJS(`setAccountInfo(${account})`);
          }, 1000);
      }
  }
</script>

监听方法

vue文件采用@message触发监听函数

<template>
   <web-view @message="handleMessage" src="http://www.juejin.com"></web-view>
</template>
<script>
   export default {
       methods: {
           handleMessage(data) {
               console.log(data)
          }
      }
  }
</script>

nvue

获取webView示例

在nvue获取webView实例就很流畅了,直接通过this.$refs.webview就能获取到。

<template>
   <web-view ref="webview" src="http://www.juejin.com"></web-view>
</template>
<script>
   export default {
       onReady() {
           const account = '清欢bx'
           this.$refs.webview.evalJs(`setAccountInfo(${account})`);
      }
  }
</script>

监听方法

nvue文件采用@onPostMessage触发监听函数

<template>
   <web-view @onPostMessage="handleMessage" src="http://www.juejin.com"></web-view>
</template>
<script>
   export default {
       methods: {
           handleMessage(data) {
               console.log(data)
          }
      }
  }
</script>

h5 端

发送数据

需要引入一个uni-app的sdk,uni.webview.1.5.4.js,最低版本需要1.5.4,可以在index.html引入,也可以在main.js引入,注意点是传递的参数必须写在data里面,也就是维持这样的数据结构。

uni.postMessage({
   data: {
     xxxxxx,
     xxxxxx
  }
});

如果是页面加载完成时就需要发送数据,需要等待UniAppJSBridgeReady钩子结束后触发postMessage;

<script>
   export default {
       mounted() {
           document.addEventListener('UniAppJSBridgeReady'function() {
               uni.webView.getEnv(function(res) {
                   console.log('当前环境:' + JSON.stringify(res));
              });
               uni.postMessage({
                   data: {
                     action'message'
                  }
              });
          });
      }
  }
</script>

如果是通过事件点击发送数据,因为这时候页面已经加载完成,不需要再去监听UniAppJSBridgeReady钩子,直接触发uni.postMessage即可。

<template>
   <view>
       <button @click="handlePostMessage">发送数据</button>
   </view>
</template>
<script>
   export default {
       methods: {
           handlePostMessage() {
               uni.postMessage({
                   data: {
                     action'message'
                  }
              });
          }
      }
  }
</script>

获取数据

获取数据的函数,需要挂载到window上,可以直接写在main.js里面,数据需要共享到具体页面内,可以使用本地村存储localStorage、事件总线eventBusvuex,根据自己的需求选择。

window.setAccountInfo = function(data) {
   console.log(data)
}

踩坑点

uni is not defined

app需要涉及到离线或者内网,索引uni.webview.js下载到本地进行引入,因为uni.webview.js已经被编译成了umd格式,在vue项目中在进行一次打包后,导致this指向不是window,所以没有把uni挂在到全局上,将this指向改为window即可。

未改造之前的代码

bc066d50add559ad35e3ce275f8a9c25.png

改造后

b25b5d93bda3d86d77bba6ede2e52927.png

或者

443288274352a8eef88029712d81d9bd.png

app向h5传递参数时,无法传递对象,并且传递的参数需要字符串序列化

在传递参数时,对象传递过去没办法识别,同时传递的参数需要执行JSON.stringify(),多个参数时,可以多个参数传递,也可以把多个参数进行字符串拼接,然后再h5端进行拆分处理。

const { accountpassword } = accountInfo;
const _account = JSON.stringify(account);
const _password = JSON.stringify(password);
setTimeout(() => {
   const webView = currentWebview.children()[0];
   webView.evalJS(`setAccountInfo(${_account}, ${_password})`);
}, 1000);

四、性能极差的canvas转图片

自定义组件库里包含手写签名组件,需要用到uni.canvasToTempFilePathcanvas转成图片,这个方法的生成基础图片大小是根据当前屏幕分辨率,在模拟器上运行感觉性能还可以,但是在真机上的性能不高,如果笔画多的话,有时需要十几秒时间,这是没办法接受的,不过也有解决方式,可以通过设置destWidthdestHeight来自定义图片生成的大小,牺牲一些图片清晰度,来提高性能。

uni.canvasToTempFilePath(
  {
     canvasIdthis.canvaId,
     destWidththis.imgWidth,
     destHeightthis.imgHeight,
     success: (res) => {
       console.log('success')
    },
     fail(e) {
       console.error(e);
    },
  },
   this,
);

小结

我目前主要负责公司uni-app移动端框架的开发,包含组件库相应的生态工具多端适配离线存储hybrid,如果你也正在做相同的事,或者在使用uni-app开发,或者在学习uni-app都可以相互探讨,在这踩坑的过程中,我会持续完善此系类文章,帮助大家和自己更好的使用uni-app开发项目,fighting~

作者:清欢bx
来源:juejin.cn/post/7156017191169556511

0 个评论

要回复文章请先登录注册