——都说微信开发多坑,没想到遇到一个天坑。
在做一个vue项目时,要用到微信JS-SDK,官方文档详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html。
将当前页面url传给后端接口,再通过返回的配置信息,用wx.config成功注入,一切看起来很顺利,在安卓上运行没问题,在iphone上测试,发现浏览器刚进入时(注册页做了注入)没报错,登录后也没错,一登录再退出就报“invalid signature”,再登录回首页(首页也做了注入),也报签名错误,一刷新又正常了。检查传的url也是当前页面url,有点奇怪。以为这只是小小的bug,没想到后来进入了一个“漫长艰辛”的探索之旅...
首先,官方文档有提到这个常见错误及解决方法,一一仔细查看过,连报错的签名都用 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具校验过,都没问题。
然后,去网上一查ios注入权限是否有不同,果然找到原来单页面应用还真的不一样!!!
在微信官方文档有这么一句话:
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
但在实际测试中,安卓并没问题,反而是iphone有问题,应该是安卓版本做了修复。此为第一坑。
继续查资料得知,在ios上,无论路由切换到哪个页面,实际真正有效的的签名url是【第一次进入应用时的URL】。
从A页面跳转到B页面时,由于是使用vue-router切换,都是操作浏览器历史记录,所以ios端微信浏览器锁定的url的还是A页面的url。
比如进入应用首页是: https://m.app.com,需要使用JSSDK的页面A是:https://m.app.com/test/123,无论从首页进入到A页面之前,中间跳转过多少次路由,最终签名有效的url还是首页url。
很多文档都是说history路由模式有问题,而hash模式没问题,但我用的hash模式依旧签名错误。此为第二坑。
再来,既然是第一次进入应用时的url,那我进入应用时就把当前url保存到vuex里面,永久保存在localstorage,每次进入都是拿这个去注入权限不就行了。
在main.js里判断是否是ios和vuex保存的这个url是否为空,为空则存入当前url:
1 router.beforeEach((to, from, next) => { 2 if (isIOS() && store.state.user.wechatSignUrl === "") { 3 store.dispatch("user/setWechatSignUrl", location.href.split("#")[0]); 4 } 5 });
打开后发现刚进入没问题,注册页也ok,登录后没错,感觉到胜利在望时,一退出登录又报错,关闭微信浏览器再进入,就一直报错。。。
经过苦苦思索,突然灵光一现,想到这里说的【第一次进入应用时的URL】,应该指的是页面加载、重载或刷新后第一次进入页面时的url,之后无论路由怎么切换url都不变,所以每次整个页面加载或刷新都要清空wechatSignUrl,在再次进入时就保存当前url,这样才能锁定真正能用来签名的url。此为第三坑。
那么为什么每次退出后的url就失效了呢?原来退出后用了页面重载,还做了微信网页授权,微信网页授权要引导用户打开授权页,用户点击后再跳回来页面算加载了一次,此时跳转回来带有code和state的这个url才是有效的签名url!!!而不再是刚进入时的url。。(吐血ing)
所以退出后的页面重载前要清空wechatSignUrl:
1 commit("SET_WECHAT_SIGN_URL", ""); //退出要重载 所以重置ios微信签名地址 2 location.reload();
在跳转到授权页之前也清空wechatSignUrl:
1 store.dispatch("user/setWechatSignUrl", ""); //此处会跳到微信授权页,页面会刷新,所以要重置签名url 2 window.location.href = wechatAuth.authUrl;
我看了下,就这两个地方需要页面刷新,其他都是路由切换,刷新前清空,刷新后再进入路由就会重新保存当前的url,这个url就肯定是有效的了。
试了下,果然,打开页面后怎么跳,怎么退出再登录,不会再报“invalid signature”。
感觉已经可以收工了是不是?当我放心地关闭页面再次点进去的时候,又报错了!!!(心累)
只好继续默默探索,关闭页面再点进去也算加载,url也不能再用之前的了,所以关闭页面前也要清空wechatSignUrl!还有微信浏览器还有个刷新功能,刷新页面前也要清空,那么怎么监听这个页面关闭和刷新功能呢?
网上说vue中监听页面刷新和关闭可以用beforeunload,所以可以用:
1 // 当浏览器界面关闭或刷新时触发该事件 2 window.addEventListener("beforeunload", () => { 3 this.$store.dispatch("user/setWechatSignUrl", ""); 4 });
在pc端微信web开发者工具可以运行,但在手机上无论怎样刷都保错!!!
继续深耕,原来用onbeforeunload来监听用户离开,浏览器可以,但是在微信中无效。微信浏览器不能用这个。此为第四坑。
经过查询,微信浏览器监听离开事件需要使用pagehide事件,关闭或刷新可用。
所以在需要调用jssdk的页面增加以下事件处理程序:
1 mounted() { 2 window.addEventListener("pagehide", this.pageHide); 3 }, 4 destroyed() { 5 //移除事件处理程序 6 window.removeEventListener("pagehide", this.pageHide); 7 }, 8 methods: { 9 //页面关闭或刷新时触发 10 pageHide() { 11 this.$store.dispatch("user/setWechatSignUrl", ""); 12 }, 13 }
此刻再测试,发现终于完美运行,无论怎么关闭刷新、跳转重载都不再弹出“invalid signature”错误,天可怜见,看了多少文档,经过多少努力,才总算从这个天坑中爬出来了...^o^y...
ps:对于网上提到的第二种方法,是在进入调用jssdk的页面之前,即beforeRouteEnter钩子函数中用直接刷新方式进入(location.href),此时页面记录的当前url就是有效的url,但实测并不成功,这样页面也会刷新抖动太厉害不够平滑过渡,不推荐使用。