最近將 riot.js 升級到了 3.0,並用上了新版本的 riot-route,原先用了一年多的 2.2.4 版本內置的 riot.route 只支持 hash 形式的 SPA 單頁面應用,riot-route 則支持 pushState。api
Hash 方式有個缺點,就是服務器不知道地址欄中 # 以後的內容,放在微信裏,就致使了未受權用戶受權後想返回原界面須要藉助 JS 來實現,致使歷史記錄多了一條,用戶按返回鍵沒法退出(返回上一頁面後又被 JS 「前進」了)。瀏覽器
PushState 方式就沒有這個問題,能夠直接用 HTTP 302 重定向過來,不影響歷史記錄和返回按鈕功能。服務器
因而在新項目中開始採用 pushState 方式。微信
不過等到應用到了微信環境中,又冒出來一個問題,經過 pushState 改變地址在 iOS 中會致使 JSSDK 的受權失敗。app
在 config 中開啓 debug,可見 invalid signature 。緣由是 location.href 中的地址變化了,可是微信客戶端認爲地址仍是打開頁面時的地址,微信「複製連接」獲得的地址能夠做爲旁證。函數
先按標準方法用當前地址計算 signature,若是失敗,再用打開瀏覽器時的地址計算 signature。ui
因爲以前已經把加載微信 JSSDK 和相關 Api 的功能獨立成函數,所以此次解決起來也比較簡單。url
function runWx(api, fn, loadError) { if (typeof api === 'string') { api = [api]; } // 用 require.js 加載 JSSDK 文件,若是你已經用 <script> 方式加載,能夠省掉這一步 require(['jweixin'], function (wx) { var url = location.href.split('#')[0]; // 將 url 發往服務器計算相應的 signature $.get('/signature', { url: url }, function (ans) { // wx.config 執行成功時調用 wx.ready(function () { wx.checkJsApi({ jsApiList: _.clone(api), // 坑,微信會改變此參數的內容 success: function success(res) { if (!res || !res.checkResult || _.any(api, function (v) { return !res.checkResult[v]; })) { alert('您的微信版本太低,沒法使用此功能,請升級微信'); return; } fn(wx); } }); }); // wx.config 執行失敗時調用 wx.error(function () { alert('受權失敗,您可能沒法使用部分功能'); }); // 校驗用的參數來自服務器 var config = { // debug: true, appId: ans.data.appId, timestamp: ans.data.timestamp, nonceStr: ans.data.nonceStr, signature: ans.data.signature, jsApiList: _.union(['checkJsApi'], api) }; // 進行校驗 wx.config(config); }); }, loadError); }
先簡單展現一下這個 runWx 函數的用法:debug
// 當須要用到特定的微信接口時,運行 runWx runWx(['uploadImage', 'chooseImage'], function (wx) { // 能夠在這裏使用 wx.uploadImage 和 wx.chooseImage 功能 wx.uploadImage(...); wx.chooseImage(...); }, function () { // 加載失敗時要作的事 })
runWx 作了這麼幾件事:code
// 解決部分機型 pushState 不能正確改變地址致使受權失敗 // 在第一次打開頁面時加載此文件,記錄當時的地址做爲原始地址 var originUrl = location.href.split('#')[0]; // 增長 tryOrigin 參數 function runWx(api, fn, loadError, tryOrigin) { if (typeof api === 'string') { api = [api]; } require(['jweixin'], function (wx) { // tryOrigin 爲真時使用原始地址 var url = tryOrigin ? originUrl : location.href.split('#')[0]; $.get('/signature', { url: url }, function (ans) { wx.ready(function () { wx.checkJsApi({ jsApiList: _.clone(api), // 坑,微信會改變此參數的內容 success: function success(res) { if (!res || !res.checkResult || _.any(api, function (v) { return !res.checkResult[v]; })) { alert('您的微信版本太低,沒法使用此功能,請升級微信'); return; } fn(wx); } }); }); wx.error(function () { // 已經試了原始地址 if (originUrl === url) { alert('受權失敗,您可能沒法使用部分功能'); return; } // 沒有使用原始地址且 signature 不匹配,嘗試用原始地址計算 signature runWx(api, fn, loadError, true); }); var config = { debug: true, appId: ans.data.appId, timestamp: ans.data.timestamp, nonceStr: ans.data.nonceStr, signature: ans.data.signature, jsApiList: _.union(['checkJsApi'], api) }; wx.config(config); }); }, loadError); }
和原來相比,主要變化有:
這個改動的主要優勢是原來用到 runWx 的地方,代碼徹底不須要進行變更,由 runWx 本身去嘗試解決問題。