在vue项目中引入jssdk所遇到的各种问题
由于在最近的项目中,需要用到扫一扫二维码签到的功能,在纯H5的页面中要实现这个是不太可能的,所以考虑用jssdk或者混合开发,由于没有微信公众号和混合开发的经验,混合开发不太现实,公司没有考虑这个,而jssdk有其他公众号平台的公司配合,所以就看看技术上能不能实现。
技术上要考虑的就是,能不能在项目中引入jssdk,然后当用户在微信端打开的时候,就能够通过jssdk来调用微信的扫一扫功能。
由于公司和公众号平台公司还没谈好,首先用微信公众号测试平台来做一下测试,开始漫长的填坑之路。
首先,jssdk是需要后台配合的,后台我用nodejs写,用的是express框架,先引用微信提供的一个三方库,wechat来通过微信的接口配置信息验证,端口号只支持80端口的
var express = require('express') var app = express() var wechat = require('wechat') app.get('/weixin', wechat('wechat',function (req, res, next) { console.log('true'); })); var server = app.listen(80,function () { var host = server.address().address var port = server.address().port console.log('服务已启动在http://%s:%s',host,port) })
其中url是我自己把内网端口地址映射到公网上的地址。token是自己在后台约定好的。
如果服务启动起来,端口映射也成功了,点击提交就会提示配置成功,否则会提示有错误。
接下来需要开始获取access_token和 jsapi_ticket 来进行签名
access_token需要APPID和appsecret来获取,通过"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+APPID+"&secret="+appsecret地址来获得,其中APPID和appsecret是你自己公众号里面提供的
获取到access_token以后,通过'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+JSON.parse(result).access_token+'&type=jsapi'地址来获取jsapi_ticket,JSON.parse的result是"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+APPID+"&secret="+appsecret这个地址中获取的的返回信息。
通常情况下,access_token需要缓存,因为access_token一天只能请求2000次。但本次只是测试用,就没有将它缓存起来了
具体代码:
var sign = require('./sign') app.post('/test/getSignature', function (req, res) { var token = 'wechat', appsecret = 'xxxxxxxxxxxxxxx', //你申请的 APPID ='xxxxxxxxxxxxxx',//你申请的id url = 'http://www.baidu.com', //JS接口安全域名 参与签名用的 Res = res; //发送https get请求 获取 access_token; if(!access_token){ https.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+APPID+"&secret="+appsecret, function(res) { var datas = []; var size = 0; res.on('data', function (data) { datas.push(data); size += data.length; }); res.on("end", function () { var buff = Buffer.concat(datas, size); var result = buff.toString(); access_token = JSON.parse(result).access_token console.log(JSON.parse(result).access_token); // 获取 jsapi_ticket //异步嵌套是不合理的 不推荐这样 使用promise https.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+JSON.parse(result).access_token+'&type=jsapi',function(res){ var datas = []; var size = 0; res.on('data', function (data) { datas.push(data); size += data.length; }); res.on('end',function(){ var buff = Buffer.concat(datas, size); var result = buff.toString() console.log(JSON.parse(result).ticket); var config = sign(JSON.parse(result).ticket,url);//当前要调用js-sdk的url config.appId = APPID; console.log(config); Res.json(config); }); }).on('error',function(e){ console.log("Got error: " + e.message); }) }); }).on('error', function(e) { console.log("Got error: " + e.message); }); } else { https.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket? access_token='+ access_token +'&type=jsapi',function(res){ var datas = []; var size = 0; res.on('data', function (data) { datas.push(data); size += data.length; }); res.on('end',function(){ var buff = Buffer.concat(datas, size); var result = buff.toString() console.log(JSON.parse(result).ticket); var config = sign(JSON.parse(result).ticket,url);//当前要调用js-sdk的url config.appId = APPID; console.log(config); Res.json(config); }); }) } });
其中sign是一个签名的函数文件,里面自动生成nonceStr,timestamp字段,然后通过签名生成signature,
有一个重要的问题,url地址,如果url没有后面的path,需要补上'/',也就是说,如果你js的安全域名是http://www.abc.com,你需要写的是http://www.abc.com/,我在这儿一直被坑了很久
时间戳需要精确到秒,而不是毫秒,
url需要包含?后面的但不包含#后面的内容
具体代码如下:
var createNonceStr = function () { return Math.random().toString(36).substr(2, 15); }; var createTimestamp = function () { return parseInt(new Date().getTime() / 1000) + ''; }; var raw = function (args) { var keys = Object.keys(args); keys = keys.sort() var newArgs = {}; keys.forEach(function (key) { newArgs[key.toLowerCase()] = args[key]; }); var string = ''; for (var k in newArgs) { string += '&' + k + '=' + newArgs[k]; } string = string.substr(1); return string; }; var sign = function (jsapi_ticket, url) { var ret = { jsapi_ticket: jsapi_ticket, nonceStr: createNonceStr(), timestamp: createTimestamp(), url: url }; var string = raw(ret); jsSHA = require('jssha'); shaObj = new jsSHA(string, 'TEXT'); ret.signature = shaObj.getHash('SHA-1', 'HEX'); return ret; }; module.exports = sign;
到此,后台获取前端需要的wx.config的所有字段完成。
然后前端开始请求后台的接口,需要注意的是,前端的地址需要添加到安全域名中,为此我映射了第二个端口到公网
然后需要在vue项目中引入jssdk,微信为了方便用户使用,将官方的jssdk发布到了npm上,有一个叫weixin-js-sdk的,但我们需要使用的不是这个,网上很多在vue中引用的是这个,但是这个是为commonjs发布的版本,只能通过require引入,很多人发现在vue中引入import wx from 'weixin-js-sdk',console.log(wx)会出现undefined,实际为了方便es6使用,官方发布了一个weixin-jsapi,这个才是我们能在vue中引用的jssdk。我也在这里被坑了很久。
然后拿到后台返回的配置参数,通过wx.config来验证配置后,在wx.ready里面就可以调用jssdk提供的api了,具体代码:
import wx from 'weixin-jsapi' export default { data () { return { videoSrc: '' } }, mounted () { http.POST('/xxxx/xxxxxxxxx').then((data) => { console.log(wx) wx.config({ debug: true, appId: data.appId, // 必填,公众号的唯一标识 timestamp: data.timestamp, // 必填,生成签名的时间戳,精确到秒 nonceStr: data.nonceStr, // 必填,生成签名的随机串 signature: data.signature, // 必填,签名 jsApiList: ['scanQRCode'] }) }).catch(() => { alert('failed') }) }, methods: { handleQR () { wx.ready(() => { console.log('ininin') wx.scanQRCode({ needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有 success: function (res) { var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果 } }); }) } } }