hybrid 即“混合”,是客户端(app)和前端混合开发,需要前端开发人员和客户端开发人员配合完成,某些环节也可能涉及到server端。
PS:不要以为自己是前端开发就不学习客户端或者server端的知识。
基础知识:
webview是app中的一个组件(app可以有webview,也可以没有),用于加载h5页面,即一个小型的浏览器内核。
schema协议是前端和客户端通讯的约定。每个app可能不一样,协议前面的名称都是app自己可以定的。比如:
前端要遵守这些协议才能和客户端交互,
file:// 协议是用来加载本地文件(html,css,js)用的协议,即使是断网了依然可以加载这些资源,加载速度快。hybrid用的就是file://协议,因为快,file://协议后面跟文件的绝对路径。
http(s)协议用来加载远程文件,需要网络,会相对慢点。
hybrid存在的价值:
- 可以快速迭代,无需app审核。
审核再快都得一两天的样子,苹果strore审核得一个周的样子,但用了hybrid可以在不需要app审核得情况下一天上线很多次都没问题。app需要审核是因为开发app的代码有权利访问手机中涉及到用户安全和隐私的内容,比如深层次的api像地理位置、开启相机。hybrid用前端开发的那部分只是上线前端代码(html,css,js),前端代码是没有能力获取用户的地理位置、摄像头拍照扫码等信息的,反正前端代码没这么高的权限想捣乱都没办法就不用审核了。
- 体验流畅(和NA体验基本类似)
- 减少开发和沟通成本,双端公用一套代码
hybrid更新方法:
前端做好静态页面(html,js,css),将文件交给客户端,客户端拿到前端静态页面后以文件形式存储在app中,客户端在一个webview中使用file://协议
加载静态文件。用户每次打开app,app都去server端看看有没有最新的前端静态文件,如果有就下载下来然后覆盖本地旧的前端静态文件,替换每个客户端的静态文件只能客户端来做,让客户端去server下载最新的静态文件,前端可以维护server的前端静态文件,把最新的前端文件上传给客户端。
- 分版本,有版本好,如201910111015
- 将静态文件压缩成zip包,上传到服务端
- 客户端每次启动都去服务端检查版本号
- 如果服务端版本号大于客户端版本号,就去下载最新的zip包
- 下载完后解压包,然后将现有文件覆盖
注意:服务端的版本和zip包的维护;用户在客户端更新zip包前先对比版本号,没必要启动一次下载一次;zip包下载解压和覆盖。
hybrid和h5的区别:
h5在加载的时候会有一小会儿的白屏,页面上方会出现一个加载条,但在hybrid中这些都没有。所以hybrid相对h5更快更流畅。hybrid产品功能稳定,体验要求高,迭代频繁。h5大多只在单次的运营活动或不常用功能。
前端和客户端通讯:
新闻详情页适用hybrid,前端获取新闻内容不能用ajax获取,ajax获取的接口是http(s)协议,而hybrid本地用的是file://协议,协议不一样会跨域,而且http(s)线上的地址速度慢。所以用客户端获取新闻内容(这里客户端是可以提前获取新闻内容的),然后js通讯拿到内容再渲染,
基本形式:
js访问客户端能力,传递参数和回调函数,客户端通过回调函数返回内容。
封装schema方便前端使用
invoke.js
//自执行函数,防止污染全局变量
(function (window, undefined) {
// 调用 schema 的封装
function _invoke(action, data, callback) {
// 拼装 schema 协议
var schema = 'myapp://utils/' + action
// 拼接参数
schema += '?a=a'
var key
for (key in data) {
if (data.hasOwnProperty(key)) {
schema += '&' + key + data[key]
}
}
// 处理 callback
var callbackName = ''
if (typeof callback === 'string') {
callbackName = callback
} else {
callbackName = action + Date.now()
window[callbackName] = callback
}
schema += 'callback=callbackName'
// 触发
var iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = schema // 重要!
var body = document.body
body.appendChild(iframe)
setTimeout(function () {
body.removeChild(iframe)
iframe = null
})
}
// 暴露到全局变量
window.invoke = {
share: function (data, callback) {
_invoke('share', data, callback)
},
scan: function (data, callback) {
_invoke('scan', data, callback)
}
login: function (data, callback) {
_invoke('login', data, callback)
}
}
})(window)
schema.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<title>Document</title>
</head>
<body>
<button id="btn1">扫一扫</button>
<button id="btn2">分享</button>
<script type="text/javascript" src="./invoke.js"></script>
<script type="text/javascript">
// function invokeScan() {
// window['_invoke_scan_callback_'] = function (result) {
// alert(result)
// }
// var iframe = document.createElement('iframe')
// iframe.style.display = 'none'
// // iframe.src = 'weixin://dl/scan' // 重要!
// iframe.src = 'weixin://dl/scan?k1=v1&k2=v2&k3=v3&callback=_invoke_scan_callback_'
// var body = document.body
// body.appendChild(iframe)
// setTimeout(function () {
// body.removeChild(iframe)
// iframe = null
// })
// }
document.getElementById('btn1').addEventListener('click', function () {
// invokeScan()
window.invoke.scan({}, function () {})
})
document.getElementById('btn2').addEventListener('click', function () {
window.invoke.share({
title: 'xxx',
content: 'yyy'
}, function (result) {
if (result.errno === 0) {
alert('分享成功')
} else {
alert(result.message)
}
})
})
</script>
</body>
</html>
内置上线:
将以上封装的invoke.js代码打包,内置到客户端,客户端每次启动webview都默认执行invoke.js,本地加载免去网络加载的时间,更快,本地加载没有网络请求黑客看不到schema协议,更安全。scheme协议就是用来规定前端和客户端如何进行通讯,它可以通过iframe来使用,内置上线的好出就是更快更安全。