重构老旧坑多娃(Cordova)应用 | 项目复盘

1,957 阅读3分钟

重构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
        }