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协议,更安全。

 

posted @ 2019-07-01 18:36  wzndkj  阅读(925)  评论(0编辑  收藏  举报