解決vue單頁面應用做微信JSSDK注入許可權時出現“invalid signature”(ios端)
——都說微信開發多坑,沒想到遇到一個天坑。
在做一個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,但實測並不成功,這樣頁面也會重新整理抖動太厲害不夠平滑過渡,不推薦使用。