详解IOS微信上Vue单页面应用JSSDK签名失败解决方案
背景
手机型号:
型号:iphone 7 / iphone xs max
版本:ios 10.3.1 / ios 12.1
微信版本:wechat 6.7.3
问题还原:
vue应用(vue-router)上使用history模式,在某个页面内调用微信jssdk相关api,如扫码、分享等,使用当前页面url总会出现签名错误(invalid signature),导致api调用失败。
问题根源
vue-router进行路由切换的时候,总是会操作浏览器的历史记录,从而响应页面url变化。
在jssdk文档页面有这么一句话:
同一个url仅需调用一次,对于变化url的spa的web app可在每次url变化时进行调用,目前android微信客户端不支持pushstate的h5新特性,所以使用pushstate来实现web app的页面会导致签名失败,此问题会在android6.2中修复
但根据多次测试情况来看,情况恰好相反,在android下直接使用 window.location.href 得出的url进行签名是完全没问题(可能已升级至android6.2以上版本),在ios上就不行了。
这是因为在ios上,无论路由切换到哪个页面,实际真正有效的的签名url是【第一次进入应用时的url】。
比如进入应用首页是: https://m.app.com,需要使用jssdk的页面a是:https://m.app.com/product1/123,无论从首页进入到a页面之前,中间跳转过多少次路由,最终签名有效的url还是首页url。
解决方案
方案一
直接粗暴处理方式:
在进入需要使用jssdk页面(b)的前一个页面(a),即 a > b,直接使用 window.location.href 或 window.open 打开b页面,此时因为b页面是直接刷新方式进入,所以当前b页面url无论ios或android都是可以直接拿来签名的。
这种方式处理缺点也很明显,如页面刷新抖动太厉害不够平滑过渡,再比如b页面需要从vuex中取出缓存信息,如果这些缓存信息不是通过vuex保存在localstorage的话,取出来的肯定都是空的。
方案二
思路:既然ios仅可使用第一次进入应用的url来签名,那么在vuex上缓存一个微信签名url,ios保存第一次进入应用的url,android则缓存为每个页面的url。签名时,直接从缓存拿出签名url来处理。
// 全局判断是否ios方法 function isios(){ const u = navigator.useragent; return u.indexof("iphone") > -1 || u.indexof("mac os") > -1; }
1. 定义vuex缓存
... { state: { wechatsignurl: "" }, mutations: { setwechatsignurl(state, wxsignurl) { // 关键点 // ios仅记录第一次进入页面时的url // ios微信切换路由实际url不变,只能使用第一进入页面的url进行签名 if (isios() && state.wxsignurl !== '') { return; } state.wxsignurl = wxsignurl; } }, getters: { getwechatsignurl: (state) => state.wxsignurl } } ...
关键点在于设置更新微信签名url判断的地方:首次进入应用页面的时候肯定会触发更新,若是ios且签名url已经设置过了,那么就不需要更新设置了,只要不退出或刷新应用,缓存永远都会是首次进入页面url。
2. 路由守卫内触发更新签名url
import store form "@/stores" // 获取真实有效微信签名url function getwechatsignurl(to){ if(isios()) { return window.location.href; } else { // 此处$apphost需要自行处理 return $apphost + to.fullpath } } ... $router.beforeeach((to, from, next) => { store.commit("setwechatsignurl", getwechatsignurl(to)); }) ...
在路由守卫内更新签名url,保证ios是使用当前页面url,android是使用目标路由完整地址再加上域名
3. 使用签名url调用jssdk api
在使用jssdk api的页面通过vuex取出缓存的微信签名url,然后进行签名。
比如:
import store form "@/stores" ... { methods: { initwechatshareconfig() { const that = this; const wxsignurl = store.getters['getwechatsignurl']; const wxshareconfigs = { // 微信分享配置 } // 初始化微信分享 $wechat.share(wxsignurl, wxshareconfigs); } } } ...
$wechat.share 是根据jssdk文档二次封装的分享方法,内部是根据wxsignurl先进行签名,然后再调用分享api
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。