基于vue的路由history模式下的微信内分享
[ 2018/07/10, Vue , 5634阅, 2评 ]

一个基于vue的h5项目,并使用了h5 history模式(即去掉了#号),并同时兼顾公众号内嵌(安卓环境、IOS环境)、安卓app内嵌和基本的网页使用。主要入口为公众号的菜单,当然也可以是直接点链接进入,亦或者扫码进入,也可以是从已经分享出来的链接点击再进入等等。

由于是基于vue的SPA应用,所以每个页面的标题和描述都是一样的。尽管做了动态更改title和desciption,但分享出去后的显示效果仍然达不到要求,而且ios版本的微信分享出去的链接也存在不准确的问题,基于此,决定使用微信的JSSDK。微信JS-SDK说明文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

1、登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。

公众号JS接口安全域名.jpg

公众号分享接口权限.jpg

如果是订阅号,则需要申请认证后才能使用分享接口,而认证又需要账号主体为“非个人”才行,而如果是服务号,好像是会默认开通样?!假如咱已经有一个拥有分享接口权限的公众号,并确保设置了正确的“JS接口安全域名”(右上角下拉>账号设置)

2、JSSDK引入

我使用了npm来安装npm i weixin-js-sdk -S ,当然你也可以使用其他方法引入,详见“微信JS-SDK说明文档”

3、通过config接口注入权限验证配置

“所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)” ps:然而到现在为止,安卓微信端已支持pushState的H5新特性,反而只字未提的ios没支持...下文中我会讲到解决方案。

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

上面的这个debug模式在调试的时候最好是打开,否则老半天找不到问题出在哪儿,哈哈,当然调试成功之后不要忘记关闭。

上面的几个参数appId,timestamp,nonceStr,signature都是后端接口返过来的,前端只需要传一个“调用分享功能页面的url”过去就行(不包含#及其后面部分,因为我的项目没有#号,所以就不用考虑了),后端用这个url和appId,appSecret按照微信规定的算法进行签名,因为appId,appSecret非常重要是不可能放到前端的而且签名的逻辑也比较复杂,所以只能是后端来处理了,签名算法啥的详见“微信JS-SDK说明文档”附录一,可以使用官方的微信 JS 接口签名校验工具进行校验。具体方法和坑可参考前辈们的一些文章:

4、通过ready接口处理成功验证

wx.ready(function(){
    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后
    // config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行
    // 对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});

5、通过error接口处理失败验证

wx.error(function(res){
    // config信息验证失败会执行error函数
    // 如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看
    // 对于SPA可以在这里更新签名。
});

前面说了,在路由的history模式下(使用了pushState的H5新特性),ios版的微信获取到的当前页面的链接是不准确的,导致最终签名失败(invalid signature),具体直接表现为:

先进入某个链接A,假设这个链接对应的页面中没做逻辑判断然后跳转页面什么的,这个时候得到的链接是准确的,为A没毛病;假如进入链接为A,而我们应用内对A做了跳转处理到B(比如A需要登录才能进,未登录跳转到登录页B),那么最终拿到的页面链接还是进入时的链接A而非B,假如再从B页面点击一个链接跳转到另一个页面C,这个时候得到的链接仍然还是A,再随便跳,仍然如此!虽然多点几次刷新能拿到真实链接,但并无用处...

究其原因:把第一次打开的页面叫做进入页,把前端路由若干次跳转(通过pushState/replaceState改变url)之后,当前打开的页面叫做当前页。微信验签使用的url(当前网页的URL,不包含#及其后面部分),在Android下取的是当前页url,在IOS下取的是进入页的url(支付目录也是一样)。

所以在安卓上的微信里随便怎么玩都是没毛病的。基于此,只要判断为IOS下的微信环境时,记录刚进来的链接就能干掉这个小bug啦!因为sessionStorage它独有的特性(如果用VUEX刷新会丢失),所以这里我用它来记录这个链接,我把下面这段代码放在main.js中:

if(checkClient.wechat() && checkClient.ios() && !window.sessionStorage.getItem('iosFirstUrl')){
    window.sessionStorage.setItem('iosFirstUrl',window.location.href)
}

用到的时候使用window.sessionStorage.getItem('iosFirstUrl')再拿回来。

最关键的部分来了,这里我新建一个wxShare.js文件,需要的时候直接引入它就行(因为项目中就两种页面需要使用分享功能,而绝大部分都不需要,所以就没必要全局引入了...),当然你也可以直接挂到vue的原型链上Vue.prototype.wxShare...这样在应用内就可以随便用了。

import { getWXShareSign } from '@/api/http.js'
import wx from 'weixin-js-sdk'
import { checkClient } from '@/common/tools'
const wxShare = (wxShareConfig)=>{
    let lastUrl;
    if(checkClient.wechat() && checkClient.ios() && window.sessionStorage.getItem('iosFirstUrl')){
        lastUrl = window.sessionStorage.getItem('iosFirstUrl')
    }else{
        lastUrl = window.location.href
    }
    if(checkClient.wechat()){
        getWXShareSign({
            url: lastUrl
        }).then(res=>{
            if(res.code==0){
                wx.config({
                    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                    appId: res.data.appId, // 必填,公众号的唯一标识
                    timestamp: res.data.timestamp, // 必填,生成签名的时间戳
                    nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
                    signature: res.data.signature,// 必填,签名
                    jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone'] // 必填,需要使用的JS接口列表
                });
                wx.ready(function(){
                    wx.onMenuShareAppMessage(wxShareConfig); //分享到微信朋友
                    wx.onMenuShareTimeline(wxShareConfig);  //分享到朋友圈
                    wx.onMenuShareQQ(wxShareConfig); //分享到QQ
                    wx.onMenuShareQZone(wxShareConfig); //分享到QQ空间
                });
            }
        })
    }
}
export default wxShare

需要用到的组件中:

import wxShare from '@/common/wxShare.js'
...
mounted(){
    wxShare({
        title: '...',
        desc: '...',
        link: window.location.href.split('?')[0],
        imgUrl: '...'
    })
}
...

上面的title是分享出去的标题,desc是描述,imgUrl是一个图片链接,link是被分享出去的url,为了避免真实链接受到污染,所以只取?左边的链接(因为我的url本身不会包含其他参数,所以可以这样做)。

有朋自远方来...评论一下呗O(∩_∩)O

  • 评论(2)

    菜菜驴 [ 回复 ]
    2019-05-30 13:45

    wxShare.js中引入的 getWXShareSign 和 checkClient 是啥?

    大强子 博主大人 [ 回复 ]
    2019-05-30 21:37

    @菜菜驴:getWXShareSign是获取后台返回签名信息的一个方法,checkClient是封装的检测客户端环境方法。