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

mac2025-06-20  31

最近做了个项目,项目本身没什么复杂的,就是其中有一个功能是要求调用手机摄像头扫描并识别二维码,最后还要打包成一个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 文档

其实我做的优化就两个,

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

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,看了各种文档,好像还没有哪个公司的文档能够让人一看就懂,照着文档做一次成功的,很多时候文档还不如别人写的博客管用,其实功能是好的,但特么的文档能不能写的用心点,能不能写的让人照着文档做能一次成功的。在文档这方面,国内公司跟国外公司真的差了不止一点半点。

最新回复(0)