hybrid
hybrid是什么,为何用hybrid?
hybrid即‘混合’,即前端和客户端的混合开发。
比如大部分app打开详情页,上面返回和三个点,底部的评论等是客户端做的。中间的页面就可以用hybrid。
为何使用hybrid,第一个可以快速迭代更新,无需app审核。如果要审核,一天一上线那是不可能的事情。为什么他不需要审核呢?之所以app需要审核,是因为app,比如开发安卓的java代码,这些代码都有权利访问到手机安全,比较隐私的一些东西,比如深层次的api,可以地理位置,比如开启相机这些。用这些功能开发出来的app肯定需要审核。hybrid就是js,html5,css,调用不到深层次的api,没有那么高的权限,所以不需要审核。
第二个体验更流畅。不需要重新加载页面。跟native的体验是基本一致的。第三个减少开发和沟通成本,双端公用一套代码。
webview是什么?
是app中的一个组件(app可以有webbiew,也可以咩有)
用于加载h5页面,即一个小型的浏览器内核
file协议
其实在一开始接触html开发,就已经使用了file协议。比如写一个html页面,双击打开的时候,就是file:///Users/xxx/xxx/index.html。请求本地的资源用的就是file协议。线上的用http(s)协议。只不过当时没有‘协议’,‘标准’等这些概念。所以file协议,本地文件,快。http协议,网络加载,慢。
hybrid具体怎么实现
1、前端做好静态页面(html,js,css),将文件交给客户端
2、客户端拿到前端静态页面,以文件形式存储在app中
3、客户端在一个webview中
4、使用file协议加载静态页面
介绍一下hybrid更新和上线的流程?
打包上传,对应一个版本号,客户端去拉到代码,拉的时候先去对比版本,版本一致说明没更新,客户端没必要重新下载。不一样就重新下载。然后app里面自己解压zip包。
hybrid和h5的区别
hybrid的优点:体验更好,跟na体验基本一致。可快速迭代,无需app审核(关键)
hybrid的缺点:开发成本高,联调、测试、查bug都比较麻烦。运维成本高,上线麻烦。
适用的场景:hybrid适合产品的稳定功能,体验要求高,迭代频繁。h5适合单次的运营活动或不常用功能,对体验要求不高,但对速度有要求,单次没必要话费很高的成本。
前端js和客户端如何通讯?
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
比如微信端这些js-sdk的一些客户端能力,这些是已经封装好的。这个就是前端js如何和客户端进行通讯。通过一个js方法直接就实现了,不需要具体是怎么实现的。
新闻详情页适合用hybrid,前端如何获取新闻内容?
不能用ajax获取。第一跨域,第二速度慢。而且一般ajax是用http协议获取线上的,而hybrid一般是用file协议,本地的。客户端获取新闻内容,然后js通讯拿到内容,再渲染。
js和客户端通讯的基本形式
wx.checkJsApi({ jsApiList: ['chooseImage'], // 需要检测的JS接口列表,所有JS接口列表见附录2, success: function(res) { // 以键值对的形式返回,可用的api值true,不可用为false // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"} } });
比如微信的这种。之前也用过其他的,也是封装好这样。js通过触发某个东西,然后传递参数+callback。客户端通过回调函数返回内容。
schema协议简介和使用
schema协议和我们之前了解的http协议,还有file协议。或者socket协议类似的格式
schema协议 —— 是前端和客户端通讯的约定。
比如微信的部分schema协议
weixin://dl/scan
weixin://dl/feedback
weixin://dl/moments
weixin://dl/chat
weixin://dl/help
weixin://dl/features
weixin://dl/games
weixin://dl/profile
...
前面这个名字是可以自己定的,取完后就是协议的一部分了。微信有严格的权限验证,外部页面不能随意使用 schema。如果是我们自己与客户端交互,代码如何实现
<body> <button id="btn">扫一扫</button> <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(() => { body.removeChild(iframe); iframe = null; }, 1000); } document.getElementById('btn').addEventListener('click'),function(){ invokeScan() } </script> </body>
主要建立个空的iframe,然后发起协议,然后客户端拿到协议并得到参数,然后通过回调返回结果。
schema使用的封装
封装,就是让调用者可以傻瓜式调用,而且不用再定义全局函数
调用者
window.invoke.share({title:'xxx',content:'xxx'}, function(result){ if (result.errno = 0) { alert('分享成功') } else { alert(result.message) } })
封装者
// 分享者 function invokeShoare(data, callback){ _invoke('share', data, callback) } // 登录 function invokeLogin(data, callback){ _invoke('login', data, callback) } // 扫一扫 function invokeScan(data, callback){ _invoke('scan', data, callback) } // 暴露给全局 window.invoke = { share: invokeShare, login: invokeLogin, scan: invokeScan }
再往深一层次看
function invoke(action, data, callback) { // 拼接 schema协议 var schema = 'myapp: //utils'; schema += '/' + 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 = cation + Date.now(); window[callbackName] = callback; } schema = '&callback' + callbackName; // ifarme中掉用scheme —— 省略n行(上方已经有写) }
我们综合一下,就可以整理为下面的流程
schema.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <button id="btn1">扫一扫</button> <button id="btn2">分享</button> <script src="invoke.js"></script> <script type="text/javascript"> document.getElementById('btn1').addEventListener('click'),function(){ window.invoke.scan({}, function(){ console.log('扫码成功') }) } document.getElementById('btn2').addEventListener('click'),function(){ window.invoke.share({title:'xxx',content:'yyy'}, function(){ if(result.errno === 0){ console.log('分享成功') }else { console.log(result.message); } }) } </script> </body> </html>
invoke.js
(function(){ // 调用 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(() => { 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);
将以上封装的代码打包,叫做invoke.js,内置到客户端。客户端每次启动webview,都默认执行invoke.js。本地加载,免去网络加载的时间,更快。本地加载,没有网络请求,黑客看不到schema协议,更安全。