背景
前段时间要做一系列的测试工具,需要在多平台:iOS、android、H5、公众号、小程序都实现。功能基本一样,就是在支付步骤需要区分平台,用对应的支付方式支付。本文讨论如何用一套小程序代码实现上述5个平台的开发。
效果图(左边为小程序,右边为浏览器):
omi-mp的介绍
omi-mp是腾讯前端框架omi的一个工具集,其目的是在于将小程序代码转成H5/Web,具体可以参见omi-mp的介绍Github。
omi-mp的转换并不是完全兼容小程序所有特性的,只支持了一小部分小程序API,并且存在了一些兼容特性,因此就__需要开发者在开发小程序代码时,更多的以开发H5/Web的思路开发__。
思路图和实现步骤
先实现小程序代码
1.初始化omi-mp目录工程:
npm i omi-cli -g
omi init-mp {工程名称}
cd {工程名称}
npm install
2.把小程序项目拷贝到src-mp
目录。
3.建议边实现小程序的过程中,不断的检验生成的H5的正确性,避免在最后阶段检验,否则如果出现问题,将不好定位,本地运行H5命令:
npm start //开发
再将小程序打包成H5/Web
打包H5/Web命令:
npm run build //发布
- 发布需要确认域名,修改
package.json
文件,修改"build": "PUBLIC_URL={发布域名} node scripts/build.js"
- 如果存在部分js文件丢失,可以尝试执行
gulp copyThen
公众号直接加载H5
公众号本质上也属于H5。
iOS/android App通过内嵌网页加载H5
iOS通过MKWebView加载H5。
android通过WebView加载H5。
- H5和原生App的交互部分可以通过JSBridge或者URL拦截实现
小程序代码如何区分平台
综上所述,除去部分iOS/android的原生代码外,基本所有的逻辑都是放在小程序里,按不同平台实现不同逻辑,小程序可以通过UserAgent以及Dom区分:
- 如果Dom树不存在
window
或者document
,为小程序平台; - iOS/android App内嵌网页可以自定义特殊的UserAgent,小程序代码可以通过此来区分iOS/android App平台;
- 微信App内嵌浏览器的UserAgent会带入
MicroMessenger/
关键字,可以按此区分公众号平台; - 其余为H5/Web平台;
if (typeof window == 'undefined') {
// 小程序
} else {
if (navigator.userAgent.userAgent.indexOf('ios-app') != -1
|| navigator.userAgent.userAgent.indexOf('android-app') != -1) {
// iOS/android
} else if (this.globalData.userAgent.indexOf('MicroMessenger/') != -1) {
// 公众号
} else {
// Web
}
}
iOS/android原生代码如何桥接
1.iOS/android需要先设置特殊UserAgent让小程序代码知道是iOS/android平台
iOS:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"ios-app", @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
android:
webView.getSettings().setUserAgentString("android-app");
2.以URL拦截为例,小程序代码在当处于iOS/android平台的情况下,可以发送特殊URL,让iOS/android原生代码处理:
小程序:
if (navigator.userAgent.userAgent.indexOf('ios-app') != -1
|| navigator.userAgent.userAgent.indexOf('android-app') != -1) {
let url = 'app://pay?' + query;
window.location.href = url;
}
3.iOS/android拦截URL特殊处理
iOS:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL * url = navigationAction.request.URL;
if ([url.absoluteString hasPrefix:@"app://pay"]) {
// do something...
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
android:
webView.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("app://pay")) {
// do something...
return true;
}
return false;
}
});
omi-mp的部分缺陷和一些踩过的坑
1.目前omi-mp只支持部分小程序部分API:
- wx.request
- wx.navigateTo
- wx.navigateBack
- wx.getSystemInfo
- wx.getSystemInfoSync
- wx.setNavigationBarTitle
- this.setData
- this.triggerEvent
如果用了其他的API,那么将会在输出H5报错。
2.支持组件,但是不支持组件的函数直接调用,替代方案可以用mitt,在page和component之间用mitt消息传递。
3.如果需要同时输出H5和Web,那么需要同时绑定click
和tap
事件(小程序只能绑定tap,Web只能绑定click,H5两者都可以),但是同时绑定又将会造成在H5的情况下,click
和tap
都会回调,导致两次调用。解决办法是可以在click
和tap
的地方同时加上判断,避免两次调用:
handleTap(e) {
if (!((typeof window == 'undefined') && e.type === "tap")) {
return;
} else if (!((typeof window != 'undefined') && e.type === "click")) {
return;
}
// do something...
}
4.wxml不支持Object字段的遍历处理。
5.如果编译不过,那么确认下是不是wxml中存在了一些特殊关键字,与omi的重了,导致失败。
6.即便在H5的场景下也无法使用document.getElementById()
。但是有替代方案,只是比较麻烦。
7.还有其它缺陷,有些忘了,待补充。
结语
omi-mp是一个不错的工具,在小程序不断变大变强的今天,能做到一套小程序代码,多端运行,降低开发成本。这里尤其感谢dntzhang的大力支持,希望omi越做越好。