目录
app分类
app主要指的是在手机上运行的第三方应用程序~主要分为一下三类
- Native App (原生应用):指的是基于手机操作系统开发出来的第三方应用程序
- 需要下载(每次更新都需下载)
- ios/android 操作系统不同,代码不兼容(需要开发两套不同的代码) -> 开发慢,开发成本高
- 用户体验度好
- Web App(网页应用):指的是使用web技术来开发的app
- 不需要下载
- 跨平台的,一套代码可以在多个浏览器使用->开发快、快发成本低
- 用户体验不太好,不同的浏览器展示效果可能存在差异,部分功能无法实现
- HyBird App(混合应用)
- 将原生应用与网页应用相结合,集原生应用 与 网页应用的优点于一身
- 本质就是将网页内嵌在原生app中,然后产生一系列的交互~
h5与原生进行交互
[1] 判断h5页面打开的环境是ios/android
通过navigator.userAgent判断当前页面的运行环境
[2] JS调用ios/android的方法
js调用ios的方法
-
在ios中:在ios中定义的方法若是想被嵌入的h5页面调用
- [1] 声明一个方法 如getAppInfo
- [2] 注册对这个方法的监听
wkWebView.configuration.userContentController.add(self, name: getAppInfo)
-
在js中
在js中的调用使用固定语法
window.webkit.messageHandlers.方法名.postMessage(数据)
window.webkit.messageHandlers.getAppInfo.postMessage()
js调用android的方法
-
在android中
在android中,需要暴露出一个全局变量JSBridge,类似浏览器环境中的Window
// 获取webview的设置对象 WebSettings webSettings = mWebView.getSettings() // 设置Android允许js脚本 webSettings.setJavaScriptEnabled(true) // 暴露出JSBridge的对象到webView的全局环境 mWebView.addJavascriptInterface(getJSBridge(), 'JSBridge')
通过以上代码,只要是此webView运行的h5页面中就可以访问到这个对象了!
tips:全局环境的变量名并不是固定为JSBridge的
mWebView.addJavascriptInterface(getJSBridge(), ‘WBridge’)
通过上述代码配置的全局环境变量名为WBridge若是后续需要h5页面访问的属性或方法都可以添加在全局变量中。
-
在js中
在js中的调用是非常简单的,比如说在android中暴露全局变量为JSBridge,该变量上存在getAppInfo方法, 通过如下代码即可调用
Window.JSBridge.getAppInfo()
区别
调用ios的方法时是异步的;而调用android的方法时是同步的!
总结-示例代码
function info() {
if (navigator.userAgent.search(/(iphone|ipad|ipod)/i) >= 0) {
return 'iOS' // 手机iOs
} else if (navigator.userAgent.search(/android/i) >= 0) {
return 'Android' // 手机安卓
} else if (navigator.userAgent.search(/windows phone/i) >= 0) {
return 'WinPhone'
} else {
return 'PC'
}
}
const platform = info()
const params = {
...}
if (platform === 'iOS' && window.webkit) {
// showShareIcon 为ios抛出的方法
window.webkit.messageHandlers.showShareIcon.postMessage(params)
} else if (platform === 'Android' && window.JSBridge) {
// JSBridge为Android抛出的全局对象
window.JSBridge.showShareIcon(JSON.stringify(params))
}else {
return false
}
客户端调用js方法
若是客户端想调用web页面中的方法,需要我们先将方法暴露出去
tips: 有时客户端拿不到window, 因此需要做下兼容~
const pluginFun = (function(){
// 兼容全局对象
const _global = (function () {
return window || this
}())
const plugin = {
方法1(){
},
方法2(){
},
...
}
// 将和客户端约定的方法暴露给全局对象
for (const i in plugin) {
!('jsFunc' in _global) && (_global[i] = plugin[i])
}
}())
示例: 客户端与web交互之回调
需求场景:web页面上有提现功能,此功能的逻辑是
- [1] 在客户端调用支付宝支付通道判断是否可以提现,判断结果返回到web页面
- [2] web页面获取到是否可以提现之后:若是可以提现调取提现接口,若是不可以提现则提示不可提现原因;
在vue中可以使用$on进行事件监听,具体做法如下
- [1]调用客户端方法->判断是否可以提现,监听客户端返回结果
isDrawal(){ if(isAndroid){ if (window.xxxJSBridge) { window.xxxJSBridge.isDrawal(url) // url是支付宝url } } } vm.$on('drawal', data=>{ // 获取到客户端返回的信息data, 若是可以提现则调取提现接口,若是不可以提现则提示不可提现原因; })
- [2] 封装一个方法供客户端调用触发drawal事件
const pluginFun = (function(){ // 兼容全局对象 const _global = (function () { return window || this }()) function callbackme(data){ vm.$emit('drawal', data) } _global.drawal = callbackme }())
[3]双方约定协议(schema)
当将web页面嵌入客户端时
若是我们使用$router.push等方法去进行页面跳转时,该页面会在当前
webView(相当于是一个浏览器页面)中打开。
若是我们想要在新的webView
中打开页面,则要使用h5与客户端预先约定协议
。
h5页面进行页面跳转时,客户端通过指定的方法拦截并解析url,如果检测到是预先约定好的协议,就调用相应方法~
以下面例子进行说明
-
h5与客户端约定的协议为 bd-pst
-
若是url为web则在app内打开指定h5页面(该链接的url参数)
-
若是url为非web则打来app内指定页面
-
示例1
jumpPage(url){ location.href = 'bd-pst://web?url='+encodeURIComponent(url) // 通过一个新的浏览器打开页面 }
jumpPage(url){ this.$router.push(url) // 在当前浏览器打开 }
打开页面为h5页面 路径为 url
-
示例2
location.href='bd-pst://login'
打开app内的login页面
-
需要值得注意的是 若是使用新的webView去开开页面则tabBar中左侧返回按钮不能使用$router.back(相当于一个新的浏览器页面history中的历史记录是空的)。但是在客户端是存在跳转记录的,我们可以通过客户端的方法进行页面返回。
[4] 在web页面下载app
下载地址是客户端给我们的(android与ios下载地址不同),因此在下载之前需要先判断浏览器的所属环境
if(navigator.userAgent.search(/(iphone|ipad|ipod)/i) >= 0){
// ios
location.href = 'xxx'
}else if(navigator.userAgent.search(/android/i) >= 0){
// Android
location.href='xxx.apk'
}else{
// 跳转到应用宝下载
}
[5] 是否全屏打开页面
如上页面是h5页面需要嵌入到客户端,此页面存在navBar,那这个navBar什么时候需要h5开发人员以代码添加什么时候不需要呢?
这就涉及到配置问题了。此时需要给客户端传递参数need_full_screen
是否需要完整屏幕,若是值为false, 则h5页面不占据整个页面,如下图
h5页面的大小 = 整个屏幕 - 状态栏高度 - navbar高度
若是为true,则表示该页面占据全部屏幕,如下图
整个屏幕都是h5页面的位置,但是此时就出现了一个新的问题就是在h5页面需要设置一个padding-top,padding-top的值为 状态栏的高度
。
那么如何获取状态栏的高度?自然是在客户端获取~
举例说明:
- [1] 通过bd协议打开页面---- ‘bd-xxx://web?url=xxx?need_full_screen=1’
- [2] 客户端通过指定的方法拦截并解析url,发现url携带参数need_full_screen,则在打开该页面时在路径上拼接状态栏高度参数如xxx?need_full_screen=1&height=40
- [3] h5页面通过url参数获取高度并添加样式
需要值得注意的是在客户端的tab
页面中默认need_full_screen的值为true(即默认该页面没有navBar)
[6] 将h5页面作为客户端的tab页面
[1] tab页面膜人need_full_screen为true(具体可见**[5]是否全屏打开页面**)
[2] web页面作为客户端的tab页,切换tab的时候并不会销毁页面,也就是说再次打开页面的时候并不会走created生命周期函数,导致数据不刷新(不是最新状态)。此时可以走回调函数解决问题
- [1] 封装一个监听函数供客户端调用
const vm = new Vue()
function(){ const _global = (function () { return window || this }()) function callback(data){ vm.$emit('refresh', data) } }()
- [2] 在需要刷新的tab页面监听refresh方法
vm.$on('refresh', (data)=>{ if(data === 'refresh'){ // 重新调去接口 } })