注冊登錄

微信小程序授權(quán)登錄方案以及在Taro下利用Decorator修飾器實現(xiàn)

2018-09-14
導(dǎo)讀:選用Taro做技術(shù)框架的原因:最近公司需要開發(fā)一款新的小程序,主要是做付費知識相關(guān)的產(chǎn)品,涉及到了虛擬商品支付,對于IOS的對于虛擬商品支付的種種限制,加上類似小程序的相關(guān)...

選用Taro做技術(shù)框架的原因:最近公司需要開發(fā)一款新的小程序,主要是做付費知識相關(guān)的產(chǎn)品,涉及到了虛擬商品支付,對于IOS的對于虛擬商品支付的種種限制,加上類似小程序的相關(guān)調(diào)研,決定IOS支付的方式走h5公總號支付繞開限制,所以在框架選型上面需要一套代碼加一點兼容代碼,就可以生成小程序和H5版本的庫,考慮到本身技術(shù)棧以react為主,所以最后老大選擇了Taro進行開發(fā)

對于Taro的簡單介紹以及提供能力可以瀏覽 Taro初探

需求場景

在微信小程序里面,需要做助力、拼團等邏輯的時候,有些需要鑒權(quán)的接口等,要再用戶授權(quán)登錄完畢之后,在請求的 header 帶上用戶的 accessToken ,所以要確保這些接口在用戶登錄完成之后再開始進行請求

之所以要用戶授權(quán)登錄而不用小程序的靜態(tài)登錄方式,是因為在兼容H5的時候,登陸流程是通過公眾號登錄的,在不想產(chǎn)生多余的數(shù)據(jù)下,使用用戶的 union_id 作為唯一依據(jù),用 wx.login這種形式拿用戶的 code 登錄只能拿到 open_id ,與我們的需求不符合

UnionID機制說明 · 小程序

我們這邊與后端約定是先通過用戶授權(quán) wx.getUserInfo ,拿到用戶信息發(fā)送給后端進行注冊或者登陸,后端返回一個 accessToken 作為用戶的憑證,調(diào)用其他接口的時候在 header 帶著這個 accessToken ,后端就能在需要的時候根據(jù) accessToken 獲取到當(dāng)前用戶信息

小程序的登錄流程如下

 

由于小程序的生命周期機制,生命周期是異步執(zhí)行的,生命周期之間是無法阻塞執(zhí)行,如果在 onLaunch 的時候進行用戶登錄的邏輯,在弱網(wǎng)的情況下,會出現(xiàn)一種情況就是用戶登錄沒完成的情況下,還沒拿到 accessToken 就開始了page里面的請求接口,這樣會導(dǎo)致接口報錯

解決思路

利用修飾器 Decorator 、React的高階組件 HOC 以及 async/await ,劫持當(dāng)前頁面調(diào)用接口的聲明周期,等待封裝好的用戶登錄邏輯執(zhí)行完以后,再進行當(dāng)前聲明周期里面其他調(diào)用的執(zhí)行。

舉個例子

在分享助力的場景下,新用戶點擊分享用戶的卡片進來小程序,需要彈出一個授權(quán)彈框等用戶授權(quán)登陸成功以后,才能進行助力接口的調(diào)用。

要注意的是,劫持的是當(dāng)前聲明周期的方法,并不會阻塞到其他生命周期,例如劫持 willMount 的時候, didShow 、 didMount 等周期依然會照樣按順序執(zhí)行,并不會等待 willMount 結(jié)束后再進行

代碼分享

主要分享修飾器的使用以及作用,登陸邏輯主要參考流程圖即可,代碼暫不做分享

寫一個能劫持傳入組件生命周期的修飾器

由于Taro暫時不支持無狀態(tài)組件,所以只能使用HOC的反向劫持能力,繼承傳入的組件,這個時候就可以通過等待登錄邏輯完成,再執(zhí)行劫持的生命周期

withLogin.js
const LIFE_CYCLE_MAP = ['willMount', 'didMount', 'didShow'];

/**
 *
 * 登錄鑒權(quán)
 *
 * @param {string} [lifecycle] 需要等待的鑒權(quán)完再執(zhí)行的生命周期 willMount didMount didShow
 * @returns 包裝后的Component
 *
 */
function withLogin(lifecycle = 'willMount') {
  // 異常規(guī)避提醒
  if (LIFE_CYCLE_MAP.indexOf(lifecycle) < 0) {
    console.warn(
      `傳入的生命周期不存在, 鑒權(quán)判斷異常 ===========> $_{lifecycle}`
    );
    return Component => Component;
  }
    
  return function withLoginComponent(Component) {
    // 避免H5兼容異常
    if (tool.isH5()) {
      return Component;
    }
      
    // 這里還可以通過redux來獲取本地用戶信息,在用戶一次登錄之后,其他需要鑒權(quán)的頁面可以用判斷跳過流程
    // @connect(({ user }) => ({
    //   userInfo: user.userInfo,
    // }))
    class WithLogin extends Component {
      constructor(props) {
        super(props);
      }

      async componentWillMount() {
        if (super.componentWillMount) {
          if (lifecycle === LIFE_CYCLE_MAP[0]) {
            const res = await this.$_autoLogin();
            if (!res) return;
          }

          super.componentWillMount();
        }
      }

      async componentDidMount() {
        if (super.componentDidMount) {
          if (lifecycle === LIFE_CYCLE_MAP[1]) {
            const res = await this.$_autoLogin();
            if (!res) return;
          }

          super.componentDidMount();
        }
      }

      async componentDidShow() {
        if (super.componentDidShow) {
          if (lifecycle === LIFE_CYCLE_MAP[2]) {
            const res = await this.$_autoLogin();
            if (!res) return;
          }

          super.componentDidShow();
        }
      }
    }
      
    $_autoLogin = () => {
      // ...這里是登錄邏輯
    }
  }
}

export default withLogin;
復(fù)制代碼
注意

使用的組件內(nèi)必須有對應(yīng)定義的生命周期,而且 不能使用箭頭函數(shù)式 ,例如 componentWillMount(){} 不能寫成 componentWillMount = () => {} ,會劫持失敗

需要登錄鑒權(quán)頁面的使用方式pages/xxx/xxx.js
import Taro, { Component } from '@tarojs/taro';
import { View } from '@tarojs/components';
import withLogin from './withLogin'

@withLogin()
class Index extends Component {
  componentWillMount(){
    console.log('Index willMount')
    // 需要帶accessToken調(diào)用的接口等 
  }
    
  componentDidMount(){
    console.log('Index didMount')  
  }

  render() {
    console.log('Index render');

    return <View />;
  }
}

export default Index;
復(fù)制代碼
注意
  1. 如果在繼承的時候使用了redux去connect了數(shù)據(jù),使用之后已自動為組件的props附帶上connect的數(shù)據(jù),被修飾的組件不需要再connect去拿這一個數(shù)據(jù), 不然可能會出現(xiàn)報錯 Setting data field "xxx" to undefined is invalid .

利用修飾器這個特性,我們還可以對小程序做一層瀏覽打點,分享封裝等操作

重磅推薦:小程序開店目錄

第一部分:小商店是什么

第二部分:如何開通一個小商店

第三部分:如何登錄小商店

第四部分:開店任務(wù)常見問題

第五部分:小商店可以賣什么

第六部分:HiShop小程序特色功能

第七部分:小程序直播

第八部分:小程序收貨/物流

第九部分:小程序怎么結(jié)算

第十部分:小程序客服

第十一部分:電商創(chuàng)業(yè)

第十二部分:小程序游戲開發(fā)

電話咨詢 微信咨詢 預(yù)約演示 0元開店