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
原来是有值的,经过排查,是因为添加了微信小程序打开指定页面,导致记录当前页面数据。
烦到吐血的网络调试
网络调试对于我们的日常开发是很重要的,有助于快速判断资源请求问题,但uni-app
在这方面有很大的缺陷,在讨论这个问题时,先来看一下uni-app
的真机调试方式。
终端调试工具
当项目运行时,点击终端上的调试按钮,会弹出一个调试界面。
从调试面板中,可以看到仅有Console
、Elements
、Sources
三个选项,期待许久的Network
并没有出现,这种调试方式没办法实现网络请求调试。
webview调试控制台
点击工具栏的 运行
-> 运行到手机或模拟器
-> 显示webview调试控制台
会出现一个跟谷歌浏览器一样的调试界面,虽然这里有Network
,但是很可惜,这个功能存在问题,没办法监听到网络请求。
Fiddler 抓取网络请求
在走投无路之下,只能另辟蹊径,借助工具,抓取真机的网络请求,接下来阐述一下怎么使用Fiddler
抓取真机的网络请求,配置完需要重启才生效。
下载Fiddler
这是一个免费工具,自行在网络上下载即可。
Fiddler 基础配置
点击工具栏的tools
,选择options
就会弹出一个配置界面
HTTPS 配置
选择HTTPS
选项,勾选选矿中的Capture HTTPS CONNECTs
、Decrypt HTTPs trffic
、Ignore server certificate errors
。
Connections 配置
这边配置的端口号后面配置代理的时候需要使用到。
手机配置代理
注意需要和电脑连接同一网络,点击进入手机WIFI详情界面,有个代理,选择手动模式,输入电脑的IP地址和Fiddler的监听端口,即可拦截到真机的所有网络请求,包含我们app对应的网络请求。
过滤
这边可以选择过滤对应的ip或域名,多个的话通过分号隔开即可。
爬坑许久的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: {
xxx: xxx,
xxx: xxx
}
});
如果是页面加载完成时就需要发送数据,需要等待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
、事件总线eventBus
、vuex
,根据自己的需求选择。
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即可。
未改造之前的代码
改造后
或者
app向h5传递参数时,无法传递对象,并且传递的参数需要字符串序列化
在传递参数时,对象传递过去没办法识别,同时传递的参数需要执行JSON.stringify()
,多个参数时,可以多个参数传递,也可以把多个参数进行字符串拼接,然后再h5端进行拆分处理。
const { account, password } = 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.canvasToTempFilePath
将canvas
转成图片,这个方法的生成基础图片大小是根据当前屏幕分辨率,在模拟器上运行感觉性能还可以,但是在真机上的性能不高,如果笔画多的话,有时需要十几秒时间,这是没办法接受的,不过也有解决方式,可以通过设置destWidth
、destHeight
来自定义图片生成的大小,牺牲一些图片清晰度,来提高性能。
uni.canvasToTempFilePath(
{
canvasId: this.canvaId,
destWidth: this.imgWidth,
destHeight: this.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