Vue 爬坑之旅 -- 在vue单页应用中利用 H5+ 实现扫码功能

最近做了个项目,项目本身没什么复杂的,就是其中有一个功能是要求调用手机摄像头扫描并识别二维码,最后还要打包成一个App。
基于以上的需求,最后决定采用的技术方案是前端页面用 vue 来写,然后涉及到扫码和打原生App的话就用 HBuilder 的 wap2app 的方式,综合时间和开发成本上的考虑,这种方案是最合适的。

前端界面没什么说的,这里要说的是在实现扫码功能和 App 打包的时候会碰到的坑,在这里记录一下,以避免再次踩坑。

vue 项目打包成 App

要实现扫码功能,需要先用 Hbuilder 生成一个 App 壳子出来,实际上这个壳子里面就是一个原生的 webview,我们的 vue 程序是通过 webview 加载的。所以这里先说 vue 程序打包成 App 时候要注意的地方。

选择合适的转换 App 的方式

实际上,HBuilder 将 web 程序转为 App 有两种方式:

  • WapApp:这种方式是先将 vue 程序部署到自己的服务器上,分配一个访问地址,然后在 HBuilder 中填入该地址,点创建,HBuilder 会生成一个 App,这个 App 里面其实啥都没有,你打开创建的项目目录就只能看到几个配置文件。其实它的本质就是创建一个 App 的壳子,然后在里面创建一个 webview 来加载我们创建项目时输入的链接。
  • 5+ App:这种方式其实跟上面那个本质上还是一样,唯一的区别是我们将 vue 打包后的所有文件放在生成的壳子 App 里面,就是少了服务器部署和 webview 从服务器上读取资源的过程,直接是加载的本地资源,这个的好处就是会比第一种方式加载速度上要快一些,但缺点也同样明显,就是程序更新跟纯原生的 App 更新一样需要先下载更新包安装后才能更新。

说完了区别我相信大部分人会选择第一种方式,没啥,就为了更新方便。

sitemap.json 文件的配置

用 Wap2App 方式打包的 App,里面有个 sitemap.js 文件,这个文件控制着 App 的一些基础功能,为了增强用户体验,需要对这个文件里面的配置项做一些修改。sitemap 文档

其实我做的优化就两个,

  1. 关掉退出 App 时候 toast 里面的反馈意见链接,这个反馈意见是反馈给 HBuilder 的,所以我么的用户并不需要看到这个,普通用户看见了反馈了也没用。。。
  2. 针对安卓的物理返回键做处理,安卓有物理返回键功能,按一下返回上一个页面,需要支持一下这个功能。

Talk is cheap,show you the code!

	{
	"global": {
		......
		"easyConfig": {
			"quit": {
				"toast": {
					"showFeedback": false //不显示“反馈意见”链接,默认为true  
				}
			}
		}
	},
	"pages": [{
		......
		"easyConfig": {
			"back": {
				"history": true //允许执行history.back  
			}
		}
	}]
}

其实就是分别在 global 和 pages 节点下配置 easyConfig ,一个处理退出 App 时的提示,一个处理物理返回键。

扫码

上面说完了 App 打包的事情,现在有了 App 壳子,就可以依据该壳子提供的 H5+ runtime 来实现扫码功能了,它的工作原理就是封装了一系列的 api,利用 js 和原生通信的能力,调起原生的摄像头,扫描二维码并进行解析。原理很简单,但是要自己实现这一整套过程还是很复杂的,所以就直接把他们的拿过来用了。。。。

通常情况下,App 中的扫码功能就是通过点一个按钮,然后打开扫码页面,拿到了扫码结果后执行相应的操作并关掉扫码页面。所以我就单独准备了一个扫码的页面,App 中要用到扫码功能的时候先跳到这个页面,然后扫码并执行相应的逻辑。

<template>
  <div class="scan">
    <div id="bcid">
      <div style="height:40%"></div>
      <p class="tip">...载入中...</p>
    </div>
  </div>
</template>

<script>
  /**
   * h5+ 扫码功能实现
   */
  let scan = null
  export default {
    name:'Scan',
    data () {
      return{
        fromRouter:'',//进入扫码页面的上一个路由
      }
    },
    
    beforeRouteEnter (to, from, next) {
      next(vm => {
        // 通过 `vm` 访问组件实例,记录上一个页面的路由,
        vm.fromRouter = from.fullPath
      })
    },
    mounted() {
      this.startRecognize();
    },
    beforeDestroy(){
      this.closeScan();
    },
    
    methods: {
      // 创建扫描控件
      startRecognize() {
        let that = this
        if (!window.plus) return
        scan = new plus.barcode.Barcode('bcid')
        scan.onmarked = onmarked
        that.startScan()
        
        function onmarked(type, result, file) {
          switch (type) {
            case plus.barcode.QR:
              type = 'QR'
              break
            case plus.barcode.EAN13:
              type = 'EAN13'
              break
            case plus.barcode.EAN8:
              type = 'EAN8'
              break
            default:
              type = '其它' + type
              break
          }
          // 获得code
          result = result.replace(/\n/g, '')
          if(result){
            // alert(result)
            // alert(that.fromRouter)
            // 成功,关闭控件,带参数跳转到正常页面去
            if (result.indexOf('merchantNo=') > -1) {
              //如果扫码结果中包含有商户ID,就截取ID拼接到商户确权路由中并跳转
              let merChantId = result.substr(result.lastIndexOf('=') + 1)
              that.$router.replace(`/home/merchantConfirm/${merChantId}`);
            }else if (result.indexOf('0x') === 0) {
              // alert('address')
              //如果扫码结果是钱包地址,则保存该地址并返回上一个页面
              that.$store.commit('setWalletAddress',result)
              // alert('setWalletAddress---' + that.$store.state.walletAddress)
              that.$router.replace(that.fromRouter)
            } else {
              that.$router.replace(that.fromRouter)
            }
          }else{
            // 失败,关闭控件,重新扫描
            that.myUtils.showToast(that,'二维码识别失败,请重试');
            that.$router.replace(that.fromRouter)
          }
          that.closeScan();
        }
      },
      // 开始扫描
      startScan() {
        if (!window.plus) return
        scan.start()
      },
      // 关闭扫描
      cancelScan() {
        if (!window.plus) return
        scan.cancel()
      },
      // 关闭条码识别控件
      closeScan() {
        if (!window.plus) return
        scan.close()
      }
    },
  }
</script>

<style lang="less">
  .scan {
    height: 100%;
    #bcid {
      width: 100%;
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      text-align: center;
      color: #fff;
      background: #ccc;
    }
  }
</style>

代码简单,而且也有注释,就不多 BB 了。这里还有一个小点要注意的就是,在进入和离开扫码页面的时候,要使用 replace 方法切换路由,不然在离开这个页面后会出现要按两下返回键才会返回的问题。

这里我不得不吐槽下各个互联网公司写文档的哥们,开发这么多年,接入了各种 SDK,看了各种文档,好像还没有哪个公司的文档能够让人一看就懂,照着文档做一次成功的,很多时候文档还不如别人写的博客管用,其实功能是好的,但特么的文档能不能写的用心点,能不能写的让人照着文档做能一次成功的。在文档这方面,国内公司跟国外公司真的差了不止一点半点。

你可能感兴趣的:(Vue,爬坑之旅)