一:前言
最近在做项目时候需要实现微信扫码支付功能,已经在后端获取到了微信服务器返回的Code_url,但是却难倒在了前端如何将Code_url转换成二维码的问题上(还怎么做全干工程师!),具体问题为获取不到用来装载二维码的div元素对象,从早上弄到下午,无果,晚上经大佬指点了一下豁然开朗,现将此问题记录下来希望有遇到同样问题的人看到我这篇文章后能少走弯路。
二:问题引入
在项目中我用到的生成二维码工具是QRCode.js,其帮助文档可以查看这里,要在静态页面中实现并不难,只需要将其相关函数直接拷贝下来即可运行,但是在我的项目中我的需求是当用户点击“支付”按钮时候就会弹出dialog弹窗,弹窗里面会展现支付二维码供用户扫码支付。要成功生成二维码,就需要通过new QRCode(element, option)函数生成一个可执行对象,其中element表示显示二维码的元素或该元素的 ID,option表示参数配置,我的简化代码如下:
<div id="add_order" class="app"> <button type="button" @click="ORCode">支付</button> <!-- 微信支付二维码 --> <el-dialog :visible.sync="codeDialogVisible" :show-close="false" @close="closeDialog" width="300px" center> <div ref="qrcode" style="width:100px; height:100px; margin-top:100px; margin-left:40%;"></div> 使用微信扫码支付 </el-dialog> <!-- <div ref="qrcode" style="width:100px; height:100px; margin-top:100px; margin-left:40%;"></div> --> </div> <!--ORCode--> <script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script> <script type="text/javascript" src="http://static.runoob.com/assets/qrcode/qrcode.min.js"></script> <script> new Vue({ el: "#add_order", data() { return { codeDialogVisible: false, //微信支付二维码弹窗 Code_url: 'https://www.baidu.com/' } }, methods: { //展示支付二维码 ORCode() { console.log(this.$refs.qrcode); let qrcode = new QRCode(this.$refs.qrcode, { width: 100, height: 100, correctLevel: QRCode.CorrectLevel.L }); // console.log(qrcode); qrcode.makeCode(this.Code_url); //打开二维码弹窗 this.codeDialogVisible = true }, //关闭微信支付二维码对话框 closeDialog() { this.payBtnDisabled = false }, } }) </script>
理想情况下当用户点击支付按钮时候就会调用ORCode函数,函数先生成qrcode对象,然后再调用qrcode.makeCode()方法生成二维码,最后设置弹窗属性为可见将二维码展示出来,运行结果如下:
可以看到元素对象为undefined,这也就直接导致后面程序报错,修改程序,将div单独提取出来:
<div id="add_order" class="app"> <button type="button" @click="ORCode">支付</button> <!-- 微信支付二维码 --> <el-dialog :visible.sync="codeDialogVisible" :show-close="false" @close="closeDialog" width="300px" center> <!-- <div ref="qrcode" style="width:100px; height:100px; margin-top:100px; margin-left:40%;"></div> --> 使用微信扫码支付 </el-dialog> <div ref="qrcode" style="width:100px; height:100px; margin-top:100px; margin-left:40%;"></div> </div>
运行结果如下:
可以看到成功获取到DOM并生成二维码,这时候问题来了,为什么将这个DOM放进dialog弹窗时候就获取不到呢?原因很简单,因为dialog的visiable属性初始设置为fase,这也就直接导致获取不到里面的DOM元素。接下来尝试在执行new QRCode(element, option)函数之前将visiable属性设置为true使得可以获取到其里面的DOM元素:
//展示支付二维码 ORCode() { //打开二维码弹窗(设置visible属性) this.codeDialogVisible = true console.log("显示二维码的元素(div)="+this.$refs.qrcode); let qrcode = new QRCode(this.$refs.qrcode, { width : 100, height : 100, correctLevel:QRCode.CorrectLevel.L }); // console.log(qrcode); qrcode.makeCode(this.Code_url); },
可以看到还是获取不到DOM元素,我就是在这里被卡住了,后来经过大佬提示尝试用this.$nextTick()方法,程序修改如下:
//展示支付二维码 ORCode() { //打开二维码弹窗(设置visible属性) this.codeDialogVisible = true this.$nextTick(()=>{ console.log("显示二维码的元素(div)="+this.$refs.qrcode); let qrcode = new QRCode(this.$refs.qrcode, { width : 100, height : 100, correctLevel:QRCode.CorrectLevel.L }); // console.log(qrcode); qrcode.makeCode(this.Code_url); }) },
可以看到成功运行,这里要注意的是必须将this.codeDialogVisible = true放在nextTick()函数外面,具体原因等你看完下面对nextTick函数的介绍就懂了。
三:this.$nextTick()函数介绍
**this.nextTick()**方法主要是用在随数据改变而改变的dom应用场景中,vue中数据和dom渲染由于是异步的,所以,要让dom结构随数据改变这样的操作都应该放进this.nextTick()∗∗方法主要是用在随数据改变而改变的dom应用场景中,vue中数据和dom渲染由于是异步的,所以,要让dom结构随数据改变这样的操作都应该放进this.nextTick()的回调函数中。
程序调用created()时,dom还没有渲染,如果此时在该钩子函数中进行dom赋值数据(或者其它dom操作)时无异于徒劳,所以,created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,而与created()对应的是mounted()的钩子函数则是在dom完全渲染后才开始渲染数据,所以在mounted()中操作dom基本不会存在渲染问题。
简单的理解,vue.js中this.$nextTick()就是起到了一个等待数据的作用,也就是说,将一些回调延迟,等到DOM更新之后再开始执行。再简单点说,相当于setTimeout()的作用。
例如:
1.你改变了dom元素数据,然后你又想输出dom,那你只能等到dom更新完成之后才会实现,在程序中我要修改的是visible属性,因此要获取里面的dom(即div),就必须等待其修改完成后才能获取,这也就能理解为什么我说必须将this.codeDialogVisible = true放在nextTick()函数外面。
2.通过事件改变data数据,然后输出dom,在方法里直接打印的话, 由于dom元素还没有更新, 因此打印出来的还是未改变之前的值,(体现为虽然我在第二次报错中虽然将visible属性设置成了true,但是这时候dom元素实质上还没更新完成就通过this.refs.qrcode获取DOM显然是获取不到的,这时候的visible属性还是为false),而通过this.refs.qrcode获取DOM显然是获取不到的,这时候的visible属性还是为false),而通过this.nextTick()获取到的值为dom更新之后的值,这时候就可以成功获取DOM了。
四:优化
当我将弹窗关闭再次点击支付时候,二维码会多次生成造成盒子叠加的情况,具体现象见下图:
显然这是很不友好的,改进代码如下:
new Vue({ el: "#add_order", data() { return { codeDialogVisible: false, //微信支付二维码弹窗 Code_url: 'https://www.baidu.com/', qrcode: null, } }, methods: { //展示支付二维码 ORCode() { //打开二维码弹窗(设置visible属性) this.codeDialogVisible = true this.$nextTick(() => { console.log("显示二维码的元素(div)=" + this.$refs.qrcode); if (this.qrcode != null) { this.qrcode.clear(); // 清除代码 } else { this.qrcode = new QRCode(this.$refs.qrcode, { width: 100, height: 100, correctLevel: QRCode.CorrectLevel.L }); // console.log(qrcode); //生成二维码 this.qrcode.makeCode(this.Code_url); } }) }, //关闭微信支付二维码对话框 closeDialog() { this.payBtnDisabled = false }, } })
改进措施为将qrcode放到data里面,然后在调用QRCode方法时候先判断qrcode是否为空,为空的情况下再重新生成二维码,不为空的情况下直接展示上次生成的二维码。
五:总结
有问题在网上找不到解决方法及时找大佬!有问题在网上找不到解决方法及时找大佬!有问题在网上找不到解决方法及时找大佬!虽然自己搜索也能学到东西,但是别人给你指明方向你再有针对性地进行查找学到的东西会更好,印象也会更深刻,后续我开发完微信支付这个功能后会继续发布相关文章供大家参考,喜欢的可以点点关注。