工作总结(5)

本篇总结主要涉及微信H5开发中的比较重要的内容,整理下来也是为了以后可以方便开发。

其实微信接口的使用在微信公众号文档中都有详细的讲解,所以我会整理工作中使用微信接口的代码逻辑。所有代码都是在Vuejs框架下开发的。

微信网页授权

微信网页授权是微信H5中最为重要的,因为如果授权不成功,用户也进不了页面中来。

微信网页授权分两种情况,用scope来体现:当scope为snsapi_base时,网页授权可以获得用户的openid,而且是静默授权,并自动跳转到回调页中,相当于直接进入H5页面中;当scope为snsapi_userinfo时,网页授权可以获得用户的基本信息,但是需要用户手动同意。还有一种是涉及用户关注公众号的,暂且不提。这里我们主要也是整理前一种网页授权方式。

微信公众号文档中对网页授权流程有分步骤,按照以获取用户openid为目的,操作就是引导用户打开以下链接:

1
2
3
4
5
6
7
// appid为公众号唯一标识
// redirect_uri是授权后重定向的回调链接,对应业务链接
// response_type是返回类型,请填写code
// scope是授权类型
// state为可选的参数,重定向后会带上,业务可以带一些参数
// #wechat_redirect是无论直接打开还是做页面302重定向时候,必须带此参数
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

然后静默获得用户授权之后,会跳转到以下链接:

1
2
3
4
// redirect_uri是重定向的回调链接
// code是换取access_token的票据
// state是网页授权时带上的可选参数
redirect_uri/?code=CODE&state=STATE

我在搜索相关开发博客的时候也有看到在前端完成上面跳转的,简单说就是先判断链接是否带有code参数,没有的话就把window.location.href指向网页授权链接,重定向之后获得code参数:

1
2
3
4
5
6
7
8
this.wxCode = this.$route.query.code ? code : '';
let local = window.location.href;
if (this.wxCode == null || this.wxCode === '') { // 如果获取不到code,则重定向
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodeURIComponent(local)}&response_type=${this.wxCode}&scope=${wxConfig.scope}#wechat_redirect`;
} else {
// 如果获取到code,发请求后端
/* ... */
}

但是实际开发中我们并没有在前端做这部分内容,而且交给后端做重定向。因为网页授权链接中带有appid,是公众号唯一标识,比较重要,考虑到保险起见,不放在前端代码中。

上面的代码可以看到,获取code之后,还需要后端获取access_token。文档也有提到,这部分安全级别非常高,涉及公众号secret跟用户的access_token,所以不允许在客户端请求。因此,获得code之后,我们可以把code通过接口传到服务器,在服务器进行access_token的获取。这一步可以获取到用户的openid,这个openid是用户跟公众号唯一的openid,所以非常有用。所以最终的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
computed: {
inWx () { // 判断是否在微信环境中
const userAgent = navigator.userAgent.toLowerCase();
return /micromessenger/.test(userAgent);
}
},
created () {
if (this.inWx) {
this.initInfo();
}
},
methods: {
initInfo () {
this.wxCode = this.$route.query.code;
// 由于是后端实现网页重定向跟授权的,所以其实前端只要获得code并发起请求传给服务器就可以了
/* ... */
}
}

微信SDK

首先,使用微信sdk的前提是域名已绑定微信公众号。然后步骤是:引入js文件,通过config配置权限,通过ready接口跟error接口处理验证,最后用checkJsApi接口判断接口支持情况。

引入js文件

在html文件中引入:

1
2
3
4
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
// 或
// <script src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
// 都支持https

config配置

根据微信公众号文档,config配置需要以下字段:

1
2
3
4
5
6
7
8
 wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});

由于appid是公众号唯一标识,最好不要存放在客户端,所以可以通过请求从服务器获得除debug跟jsApiList之外的信息。所以会分两步走:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 data() {
return {
jsApiList: [
'updateAppMessageShareData',
'updateTimelineShareData',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'onMenuShareQZone',
'previewImage',
'openLocation',
'getLocation'
],
checkResult: {},
wxShareConfig: {},
wxconfig_share: {
title: '', // 分享标题
desc: '', // 分享描述
link: '', // 分享链接
imgUrl: '', // 分享图标
},
};
},
methods: {
getWechatConfig () {
axios.get(api).then(res => {
this.wxConfig = res.data;
this.wxInit(this.wxConfig);
}).catch(error => {
/* ... */
}
},
wxInit(_rpdata) {
// this.jsApiList = _rpdata.jsApiList;
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: _rpdata.appId, // 必填,公众号的唯一标识
timestamp: _rpdata.timeStamp, // 必填,生成签名的时间戳
nonceStr: _rpdata.nonceStr, // 必填,生成签名的随机串
signature: _rpdata.signature, // 必填,签名,见附录1
jsApiList: this.jsApiList // 必填,需要使用的JS接口列表
});
},
}

ready接口跟error接口处理

在传入config之后,可以直接调用ready接口处理验证成功的,而error接口则是处理验证失败的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
methods: {
wxInit(_rpdata) {
// this.jsApiList = _rpdata.jsApiList;
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: _rpdata.appId, // 必填,公众号的唯一标识
timestamp: _rpdata.timeStamp, // 必填,生成签名的时间戳
nonceStr: _rpdata.nonceStr, // 必填,生成签名的随机串
signature: _rpdata.signature, // 必填,签名,见附录1
jsApiList: this.jsApiList // 必填,需要使用的JS接口列表
});
wx.ready(() => {
/* ... */
});
wx.error(function (res) {
/* ... */
});
},
}

checkJsApi接口

验证成功之后,还需要调用checkJsApi接口来判断客户端是否支持指定JS接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
wxInit(_rpdata) {
/* ... */
wx.ready(() => {
wx.checkJsApi({
this.checkJsApi().then(() => {
/* ... */
/* 这里就可以调用各种验证成功的接口了 */
});
})
}
/* ... */
},

checkJsApi() {
return new Promise((resolve) => {
wx.checkJsApi({
jsApiList: this.jsApiList, // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: (res) => {
console.log(res)
console.log(res.checkResult)
this.checkResult = res.checkResult;
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
this.checkResult = res.checkResult;
resolve();
}
});
});
},

以上完成微信SDK基础配置处理。

微信分享

微信分享这块包括了所有分享的平台,使用的也是微信SDK所提供的所有分享相关的接口。目前微信分享有以下这些接口:

1
2
3
4
5
6
7
 updateAppMessageShareData // 分享给朋友及分享到QQ接口
updateTimelineShareData // 分享到朋友圈及分享到QQ空间接口
onMenuShareTimeline // 分享到朋友圈接口(即将废弃)
onMenuShareAppMessage // 分享给朋友接口(即将废弃)
onMenuShareQQ // 分享到QQ接口(即将废弃)
onMenuShareWeibo // 分享到腾讯微博
onMenuShareQZone // 分享到QQ空间

所有接口的写法基本相同,所有这里只列举一个接口的写法作为例子。通过上面的接口验证后,就可以调用这些接口了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 wxShareInit() {
const checkResult = this.checkResult;
if (checkResult.updateAppMessageShareData) {
wx.updateAppMessageShareData({
title: this.wxconfig_share.title, // 分享标题
desc: this.wxconfig_share.desc, // 分享描述
link: this.wxconfig_share.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: this.wxconfig_share.imgUrl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
console.log('share AppMessage success!')
},
})
}
// 其他接口写法相同
/*...*/
}

微信关注

微信关注这个比较容易,其实就是跳转到历史消息页面,如果用户没有关注公众号的话,这个页面会显示关注按钮。这个链接的形式是:

1
https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=xxxxxxxx#wechat_redirect

其中,xxxxxxxx用公众号的uin id的base64编码替换。这个公众号的uin id可以在微信公众平台登入账号后,在源代码中查找uin即可找到一串数字,然后把这串数字用base64编码,获得。

不过实际的效果是这个页面的关注按钮有时会出现,有时没有,后来网上查了一下,这个页面的关注按钮已经被微信屏蔽了,除非跟微信投钱合作,才会显示。所以……

微信关注判断

微信关注的判断目前我还没有做过,但是大致的了解到判断的方案。最关键的是需要获得openid,获得access_token,然后通过获取用户基本信息接口获得用户信息,进而判断。

首先,获得openid的方法已经在微信网页授权一节有说明,故可以获得;获得access_token需要前端获取code,然后传给服务器去换access_token,所以这一步也在微信网页授权一节有说明,故也可以获得。

获取用户基本信息接口为:

1
2
// http请求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

然后根据微信公众号开发文档所介绍的,返回的数据有这些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"subscribe": 1,
"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
"nickname": "Band",
"sex": 1,
"language": "zh_CN",
"city": "广州",
"province": "广东",
"country": "中国",
"headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"subscribe_time": 1382694957,
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
"remark": "",
"groupid": 0,
"tagid_list":[128,2],
"subscribe_scene": "ADD_SCENE_QR_CODE",
"qr_scene": 98765,
"qr_scene_str": ""
}

这里可以获得到不少的信息了,这里我们关注的是subscribe这个字段,是指用户是否订阅该公众号标识。0为未关注,1为已关注。这样,我们就可以判断用户是否关注公众号了。

微信支付

微信支付有两种方案,一种是依赖上面的微信JS-SDK,再配置完成之后调用chooseWXPay方法:

1
2
3
4
5
6
7
8
9
10
11
// 官方文档
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});

可以参考上面微信分享一节的封装方案进行开发。

另一种方案是调用在微信内置浏览器才有的WeixinJSBridge内置对象。这种方案使用起来比较简单,不需要引入什么,配置什么,只要是在微信内置浏览器中,就一定能调用这个内置对象。使用的时候只要先判断是否存在WeixinJSBridge内置对象,存在则可以传入请求参数,然后再判断返回的支付信息。需要传入的参数跟上面的一样,一般需要发起支付请求给服务器,让服务器提供需要的参数,然后在去调用WeixinJSBridge内置对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 微信支付api相关配置文档
onBridgeReady (data) {
if (typeof WeixinJSBridge === 'undefined') {
this.$toast({
message: '请使用微信内置浏览器进行支付',
position: 'bottom'
});
} else {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
appId: data.appId, // 公众号名称,由商户传入
timeStamp: data.timeStamp, // 时间戳,自1970年以来的秒数
nonceStr: data.nonceStr, // 随机串
package: data.package,
signType: data.signType, // 微信签名方式:
paySign: data.paySign // 微信签名
},
res => {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
this.$toast({
message: '支付成功',
position: 'bottom'
});
/* ... */
} else {
this.$toast({
message: '支付失败',
position: 'bottom'
});
}
}
)
}
}

微信地图

在微信H5中调起微信地图也是一个常见的功能点,也是可以通过微信JS-SDK的接口来实现。

一般使用微信地图分两种情况:一种是需要打开已知地点的微信地图,方便用户对地点进行导航;另一种是需要打开用户所在位置的微信地图。

已知地点的微信地图

这里使用的为微信JS-SDK 中的openLocation接口:

1
2
3
4
5
6
7
8
9
// 官方文档
wx.openLocation({
latitude: 0, // 纬度,浮点数,范围为90 ~ -90
longitude: 0, // 经度,浮点数,范围为180 ~ -180。
name: '', // 位置名
address: '', // 地址详情说明
scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大
infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
});

根据官方文档,我们需要拿到已知地点的纬度,经度,位置名跟地址详情说明。我们可以通过以下网页获得:

1
http://www.gpsspg.com/maps.htm

然后参考前面微信分享的封装进行开发。

打开用户所在位置的微信地图

这里还需要使用另一个接口:getLocation接口。也很简单,这里直接贴代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wx.getLocation({
type: 'gcj02',
success: function (res) {
wx.openLocation({
latitude: res.latitude,
longitude: res.longitude,
name: config.name,
address: config.address,
scale: 28,
infoUrl: ''
})
},
cancel: function (res) {
console.log('cancel: ', res);
}
})

微信H5的坑

iOS中微信环境下页面有浮框软键盘隐藏后页面不能点击问题

问题是这样的:浮框中有输入框,使用了fixed布局,可能在页面正中间,也可能出现在页面底部。当输入框失去焦点时,软键盘隐藏,但是页面所有点击事件都发生错乱。

其实和这个问题跟iOS系统有关,也跟微信有关。本身iOS系统下调出软键盘就会影响页面布局,然后滚动页面,然后微信内置浏览器会在软键盘隐藏时恢复页面布局,但是并没有恢复监听事件的位置。也就是在虽然有监听事件的标签元素恢复到原位了,但是监听的区域并没有恢复,因为受到页面滚动的影响。

如何处理?分两种情况:一种情况是浮框只出现在页面第一屏,这种情况下,在软键盘隐藏的同时,操作页面滚动到顶部,即可恢复正常的监听区域:

1
2
3
scrollBack () { // 绑定在blur事件上
window.scrollTo(0, 0);
}

另一种情况是浮框在底部,这种情况主要跟iOS系统有关,会单独放在下一篇工作总结中详细整理。