現(xiàn)在的網(wǎng)站開發(fā),都繞不開微信登錄(畢竟微信已經(jīng)成為國民工具)。雖然文檔已經(jīng)寫得很詳細,但是對于沒有經(jīng)驗的開發(fā)者還是容易踩坑。
所以,專門記錄一下微信網(wǎng)頁認證的交互邏輯,也方便自己日后回查:
微信網(wǎng)頁SDK加載
在多人團隊協(xié)作中,加載資源的代碼需要格外小心。因為可能會有多個開發(fā)者在同一業(yè)務(wù)邏輯下調(diào)用,這會造成資源的重復(fù)加載。
處理方法有兩種,第一種是對外暴露多余接口,專門check是否重復(fù)加載。但是考慮到調(diào)用者每次在加載前,都需要顯式調(diào)用check()方法進行檢查,難免會有遺漏。
所以采用第二種方法--設(shè)計模式中的緩存模式,代碼如下:
// 備忘錄模式: 防止重復(fù)加載 export const loadWeChatJs = (() => { let exists = false; // 打點 const src = '//res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'; // 微信sdk網(wǎng)址 return () => new Promise((resolve, reject) => { // 防止重復(fù)加載 if(exists) return resolve(window.WxLogin); let script = document.createElement('script'); script.src = src; script.type = 'text/javascript'; script.onerror = reject; // TODO: 失敗時候, 可以移除script標簽 script.onload = () => { exists = true; resolve(window.WxLogin); }; document.body.appendChild(script); }); })();
繪制登陸二維碼
根據(jù)《微信登陸開發(fā)指南》,將參數(shù)傳遞給window.WxLogin()即可。
// 微信默認配置 const baseOption = { self_redirect: true, // true: 頁內(nèi)iframe跳轉(zhuǎn); false: 新標簽頁打開 id: 'wechat-container', appid: 'wechat-appid', scope: 'snsapi_login', redirect_uri: encodeURIComponent('//1.1.1.1/'), state: '', }; export const loadQRCode = (option, intl = false, width, height) => { const _option = {...baseOption, ...option}; return new Promise((resolve, reject) => { try { window.WxLogin(_option); const ele = document.getElementById(_option['id']); const iframe = ele.querySelector('iframe'); iframe.width = width? width : '300'; iframe.height = height? height : '420'; // 處理國際化 intl && (iframe.src = iframe.src + '&lang=en'); resolve(true); } catch(error) { reject(error); } }); };
在需要使用的業(yè)務(wù)組件中,可以在周期函數(shù)componentDidMount調(diào)用,下面是demo代碼:
componentDidMount() { const wxOption = { // ... }; loadWeChatJs() .then(WxLogin => loadQRCode(wxOption)) .catch(error => console.log(`Error: ${error.message}`)); }
回調(diào)網(wǎng)址與iframe通信
這一塊我覺得是微信登陸交互中最復(fù)雜和難以理解的一段邏輯。開頭有講過,微信二維碼渲染有2中方式,一種是打開新的標簽頁,另一種是在指定id的容器中插入iframe。
毫無疑問,第二種交互方式更友好,因為要涉及不同級層的頁面通信,代碼處理也更具挑戰(zhàn)。
為了方便說明,請先看模擬的數(shù)據(jù)配置:
// redirect 地址會被后端拿到, 后端重定向到此地址, 前端會訪問此頁面 // redirect 地址中的參數(shù), 是前端人員留給自己使用的; 后端會根據(jù)業(yè)務(wù)需要, 添加更多的字段, 然后一起返回前端 const querystr = '?' + stringify({ redirect: encodeURIComponent(`${window.location.origin}/account/redirect?` + stringify({ to: encodeURIComponent(window.location.origin), origin: encodeURIComponent(window.location.origin), state: 'login' })), type: 'login' }); const wxOption = { id: 'wechat-container', self_redirect: true, redirect_uri: encodeURIComponent(`//1.1.1.1/api/socials/weixin/authorizations${querystr}`) // 微信回調(diào)請求地址 };
前后端、微信服務(wù)器、用戶端交互邏輯
按照上面的配置,我描述一下前端、用戶端、微信服務(wù)器和后端交互的邏輯:
跨Iframe通信
前面流程走完了,現(xiàn)在的情況是頁面中iframe的二維碼區(qū)域,已經(jīng)被替換成了/account/redirect?...的內(nèi)容。
為了實現(xiàn)通信,需要在頁面的周期中監(jiān)聽message事件,并在組件卸載時,卸載此事件:
componentDidMount() { // ... ... window.addEventListener('message', this.msgReceive, false); } componentWillUnmount() { window.removeEventListener('message', this.msgReceive); } msgReceive(event) { // 監(jiān)測是否是安全iframe if(!event.isTrusted) { return; } console.log(event.data); // 獲取iframe中傳來的數(shù)據(jù), 進一步進行邏輯處理 }
而在/account/redirect?...路由對應(yīng)的組件中,我們需要解析路由中的params參數(shù),按照業(yè)務(wù)邏輯檢查后,將結(jié)果傳遞給前面的頁面:
componentDidMount() { // step1: 獲取url中params參數(shù) const querys = getQueryVariable(this.props.location.search); // step2: 檢查querys中的數(shù)據(jù)是否符合要求 ... // step3: 向頂級頁面?zhèn)鬟f消息 return window.parent && window.parent.postMessage('data', '*'); }
至此,微信網(wǎng)頁認證的流程完成。
更多:關(guān)于iframe通信的更多細節(jié),請查看MDN的文檔
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com