重构Cordova应用| 项目复盘
首先吐槽一下坑多娃(Cordova),虽然大大降低了开发App的成本。但是它的插件系统实在是离谱的很。每次添加插件,都是通过钩子的方式,直接修改原生工程里面的各种配置。官方也没有比较明确的规范,经常导致安装一个插件后另外一个装不上了。如果当时没玩明白的化还在Cordova工程里面写了大量定制代码,升级会全部被清空。
最近花一些时间给3年前的老旧混合应用换了一个壳儿。Cordova 应用的壳儿已经2年没有更新过了,在新版的手机上安装会提示此应用为旧版本安卓手机设计,客户相当的不满意。
技术选型选择了Uniapp,因为公司全没有原生开发资源。Uniapp封装好的一套符合国情的接口还是很不错的。
如果不想使用uniapp的大佬们也想给自己的坑多娃应用换个宿主,推荐使用 Capacitor (可能需要科学上网)。Capacitor构造出来的原生工程是可以维护的。不会像坑多娃一样造成应用完全无法升级。
开始重构!还我可维护的原生工程!(花了大概4天左右,成本还是比较低的)
这里选择采用类似微前端的方案。旧版本实现兼容,新的东西采用uniapp的页面进行开发,从web应用跳转到新开发的uniapp页面中。
更换web应用中Cordova的API为HTML5+的API (H5+ API文档)
// uniapp 提供了超级多的原生api基本可以替代cordova中原有的大部分api 详见上面的文档
// 旧代码
if (navigator.geolocation && cb) {
navigator.geolocation.getCurrentPosition(
p => {
var gps = {
gpsTime: Date.now(),
lat: p.coords.latitude,
lng: p.coords.longitude
}
cb(gps)
},
error => {
if (err) {
err(error)
}
},
{ maximumAge: 60000, timeout: 30000, enableHighAccuracy: true }
)
} else {
console.error('navigator.geolocation is null')
}
// 新代码
plus.geolocation.getCurrentPosition(
position => {
const {
timestamp,
addresses,
coords: { latitude, longitude }
} = position
const { lng, lat } = GCJ2GPS({ lng: longitude, lat: latitude })
cb({
longitude: longitude,
latitude: latitude,
gpsTime: timestamp,
address: addresses
})
},
err,
{
enableHighAccuracy: true,
provider: 'amap',
geocode: true
}
)
使用uniapp的webview加载应用
-
创建uniapp工程,并把原本的web应用打包 放到 hybird/html/ 目录下
-
// 添加vue.config.js const path = require('path') const CopyPlugin = require("copy-webpack-plugin"); module.exports = { configureWebpack: { // webpack所要搜索的文件 plugins: [ new CopyPlugin([{ from: path.resolve(__dirname, "./hybird"), to: "hybird" }, ]) ], resolve: { alias: {} } }, chainWebpack: config => { config.resolve.alias.set('@', path.resolve(__dirname, 'src')) config.resolve.symlinks(true) config.resolve.extensions .add('ts') .add('tsx') .add('js') .add('jsx') .add('vue') }, transpileDependencies: [] } -
// 在页面 page/index/index (注意需要添加uni.webview.1.5.2.js) // 文档 https://uniapp.dcloud.io/component/web-view <template> <view> <web-view src="/hybird/html/index.html" ref="webview" class="webview" /> </view> </template> <script lang="ts"> var wv; //计划创建的webview export default { data() { return { onShowTime: Date.now() }; }, onShow() { this.onShowTime = Date.now(); }, onLoad() { setTimeout(() => { var currentWebview = this.$scope.$getAppWebview(); wv = currentWebview.children()[0]; // 注意这个 wv.setJsFile('/static/js/uni.webview.1.5.2.js'); plus.key.addEventListener('backbutton', (source, source2) => { wv.canBack(evt => { // 刚刚显示这个页面的时候不返回 处理点击安卓返回按钮 不然在uniapp页面返回的时候也会触发这个钩子 if (evt.canBack && Math.abs(this.onShowTime - Date.now()) > 1000) { wv.evalJS(`history.back();`); } }); }); }, 1000); //如果是页面初始化调用时,需要延时一下 }, }; </script>
实现web应用与uni-app的通讯
-
/** * web应用和uni-app进行通讯 */ uniBridge(action, params) { try { uni.postMessage({ data: { action, params } }) } catch (err) { } finally { console.log('bridge called', action, params) } } -
<template> <view><web-view src="/hybird/html/index.html" ref="webview" class="webview" @message="handlePostMessage" /></view> </template> <script lang="ts"> const Alipush = uni.requireNativePlugin('Alipush'); // 阿里云推送 import LoginService from '****'; //登录逻辑 import hotpatch from '@/shared/hotpatch'; // 这个是uni热更新 https://ext.dcloud.net.cn/plugin?id=4542 var wv; //计划创建的webview export default { data() { return { onShowTime: Date.now() }; }, onShow() { this.onShowTime = Date.now(); }, onLoad() { // 用于监听webview传输的消息 setTimeout(() => { var currentWebview = this.$scope.$getAppWebview(); wv = currentWebview.children()[0]; // web内部可以调用uni接口 wv.setJsFile('/static/js/uni.webview.1.5.2.js'); plus.key.addEventListener('backbutton', (source, source2) => { wv.canBack(evt => { // 点击安卓返回按钮 && uniapp页面返回时候也会触发这个钩子,加定时hack一下 if (evt.canBack && Math.abs(this.onShowTime - Date.now()) > 1000) { // 和web应用进行通讯 wv.evalJS(`history.back();`); // wv.evalJS(`window.postMessage('xxxx')`); } }); }); }, 1000); //如果是页面初始化调用时,需要延时一下 }, computed: { // uni.postMessage web应用发出来的消息处理列表 actionMap() { return { nav: params => { // WEB路由可以跳转到用uni-app开发的功能 uni.navigateTo(params); }, login: params => { // 为了方便,直接在uni-app应用中重新登录一下。虽然性能不好,但是成本贼低 LoginService.login({ account: params.username, password: params.password }) .catch(() => { uni.showToast({ title: '登陆失败', icon: 'error' }); }) .finally(() => { // 使用uni-app的热更新 替代 cordova 的热更新 hotpatch(); }); }, bindAccount: params => { // 推送采用的阿里云推送,uniapp不带 用uni插件处理一下 if (Alipush) Alipush.bindAccount(params.userId); } }; } }, methods: { handlePostMessage(evt) { const { detail: { data } } = evt; const { action, params } = data[0]; console.log(action, params); this.actionMap[action] && this.actionMap[action].call(this, params); } } }; </script>
兼容H5开发
平时都用h5开发但是h5里面没有plus和uni接口,所以H5运行的时候跳转流程是不通的
-
/** * 改写通讯桥 * web应用和uni-app进行通讯 */ uniBridge(action, params) { try { // H5端是通过iframe实现的webview 所以可以这样搞 // 字节跳动小程序不支持、H5 暂不支持(可以直接使用 window.postMessage) // https://uniapp.dcloud.io/component/web-view 请认真阅读他们的文档 window.top.postMessage({ data: { action, params } }) uni.postMessage({ data: { action, params } }) } catch (err) { } finally { console.log('bridge called', action, params) } } -
// uni-app在3里面的代码添加这个 export default { onLoad() { // 用于监听webview传输的消息 // #ifdef H5 window.addEventListener('message', event => { this.handlePostMessage({ detail: { data: [event.data.data] } }); }); // #endif }