人人都該具有封裝思惟:Taro+Es6+Promise+Ts簡易構建微信小程序的全局請求方法

不管是app仍是小程序或者其餘端,交互中請求無處不在。node

一個優秀的封裝類,能讓你的開發效率事半功倍,因此封裝邏輯相當重要,固然我也是個小菜鳥,跟着本身的思路寫過一些封裝方法,一方面是想不足之處還望路過的大神給予指正,兩者是爲新手打開一個善於封裝思惟的大門,下面進入到前置知識。react

Api:cnodejs.org/api/v1es6

Ts: 簡易的類型聲明、接口等小程序

Es6:結構賦值、擴展運算、promise、export等api

Taro:類react,以及小程序基礎知識等promise


1.梳理Taro的請求 Taro.request,實現最簡易的請求方法bash

import Taro from '@tarojs/taro'
//回調調用
function doRequestAction(){
  Taro.request({
    url: '',
    data: {},
    header: {},
    success: res => {},
    fail: err => {},
    complete: info => {}
  })
}
// promise調用
function doRequestAction(){
  Taro.request({
    url: '',
    data: {},
    header: {}
  })
  .then(res=>{})
  .catch(err=>{})
  .finally(()=>{})
}
複製代碼

其中利弊很少作介紹,咱們將會用到promise的請求方式。網絡

2.梳理會用到的請求結構,以及定義的文件分類:app

  • 接口地址,也就是咱們常說的url,存放於api.ts文件中
  • 請求方法,也就是咱們封裝的方法,咱們存放於request.ts文件中
  • 接口類型,也就是咱們聲明數據類型的文件,咱們存放於inter.d.ts文件中
  • 配置文件,經常使用的全局請求域名,其餘不動參數等,咱們這裏只是簡單的示例所以不須要,若是有須要請你們自行配置
文件名 做用
api.ts 存放接口地址、以及統一請求域名前綴
request.ts 封裝公共請求方法、以及調用方法集合
inter.d.ts ts的聲明文件,主要存放返回值類型,請求參數類型等

3.接入promise聲明本身的方法並返回本身的promise請求對象ide

我這裏儘可能寫es6寫法讓你們在從此的項目開發中更加順暢的使用,固然在本身的項目中請結合實際狀況使用,不要盲目的接入一些新的寫法。下面進入知識點梳理:

  • 請求是否loading,真 ? 結束隱藏loading : ' '
  • loading層是否開啓mask
  • loading文字參數可自定義
  • 請求失敗是否彈出咱們的報錯信息
  • url在不以http/https的狀況下使用自定義接口
import Taro from '@tarojs/taro'
// 暫時考慮 req的屬性都會傳入
const doRequestAction = (req) => {
  return new Promise((resolve, reject) => {
    if (req.loading) Taro.showLoading({ title: req.title ? req.title : '數據加載中...' })
    Taro.request({
      url: /^http(s?):\/\//.test(req.url) ? req.url : '', //暫時留空
      method: 'POST',
      data: {},
      header: { 'content-type': 'application/x-www-form-urlencoded' }
    })
    .then(res => {})
    .catch(err => { 
        //報錯提示 
    })
    .finally(() => {
      if (req.loading) Taro.hideLoading()
    })
  })
}
複製代碼

4.分離請求參數並使用ts聲明傳入值與類型

1.將使用到的參數進行分離

2.每一個參數給出默認值,若是不傳人將用默認值代替

3.使用ts聲明參數類型

export interface RequestBase {
  url: string, //字符串
  method: 'POST' | 'GET', //常規請求方式,根據項目要求添加
  data: any, // 每次的參數都是不固定的,所以咱們暫時不聲明數據類型
  header: RequestHeader, // 下面的requestheader類型,
  loading: boolean, // 請求是否開啓loading層
  mask: boolean, //開啓loading層的狀況下是否不能點擊,全屏遮罩
  title: string, //開啓loading層的提示內容
  failToast: boolean //若是請求失敗,我是否直接彈出個人提示
}

export interface RequestHeader {
  'content-type': string // 表示content-type類型必須聲明
}
複製代碼

上面的header,我從新定義了一個接口來聲明類型,是爲了更方便的去管理這個數據,試想若是咱們平時須要將用戶的token帶入到header裏面,那麼咱們就會在RequestHeader中在聲明一個token字段。

所謂的接口也就至關於咱們這個數據裏面有什麼字段,字段是什麼類型。

因此,咱們在header中在加入token字段,實際項目中,可能還會帶入加密串,時間,以及其餘的輔助驗證字段,這裏只爲了你們方便開發作出示例,實際還需看項目聲明。

特殊提醒:ts是能夠不加逗號的

export interface RequestHeader {
  'content-type': string // 表示content-type類型必須聲明
  token: string
}
複製代碼

聲明咱們的默認參數,在用戶沒有參數傳入的狀況下,將會使用咱們的默認參數來輔助請求。

// 使用默認參數,當數據不傳入指定字段時替代
const NormalRquestData: RequestBase = {
  url: api.DOMAIN, // 默認請求地址
  method: 'GET', // 默認get請求
  header : { // 默認使用的header頭
    "content-type": 'application/x-www-form-urlencoded',
    token: ''
  },
  data: {}, // 默認沒有參數,傳入空對象
  loading: true, //默認開啓loading層
  mask: true, //請求時不須要點擊
  title: '數據加載中', //loading提示文字
  failToast: false // 通常咱們會處理相應業務邏輯,就不直接提示阻斷流程
}
複製代碼

思考問題,咱們若是每次動態 帶上咱們token? 用戶剛開始進入小程序,這時多是沒有受權,後面受權了咱們要及時更新token來達到用戶交互目的 這時咱們能夠將header提出,固然我通常會在個人狀態管理中操做,這裏作個例子爲你們提供思路。

  • 將token做爲可選字段
  • 封裝方法每次請求動態提取token
  • 方法返回類型爲咱們定義的RequestHeader類型
// inter.d.ts
export interface RequestHeader {
  'content-type': string // 表示content-type類型必須聲明
  token?: string // token可能不存在,若是存在就是字符串類型
}
//request.ts 獲取header頭 返回值是RequestHeader類型
const getRequestHeader = (): RequestHeader => {
  let token: string = Taro.getStorageSync('token')
  return token ? {
    "content-type": 'application/x-www-form-urlencoded',
    token: token
  } : {
    "content-type": 'application/x-www-form-urlencoded'
  }
}
複製代碼

5.在參數某些字段不傳入的狀況下,咱們使用本身的參數字段進行默認填充

思考問題:RequestBase參數都是必傳,可是咱們的請求的時候參數都是可傳,若是咱們將其都改成可選參數,這個時候若是咱們使用req.title(loading標題)、req.url('請求api地址') 在ts的檢測下,這些參數都是可能不存在的,這樣咱們會寫大量判斷,那麼咱們的代碼就會變得至關糟糕~!所以咱們再聲明一個接口用來可選參數的規範。

// 遍歷RequestBase中全部key,都改成可選參,這樣咱們就只管維護RequestBase
type Request = {
  [K in keyof RequestBase]?: RequestBase[K]
}
複製代碼

改造請求方法,並聲明各個類型,使ts更加規範, 將接口類使用inter導入,也將以前的api改爲Api統一首字母大寫

import Taro from '@tarojs/taro'
import * as Api from './api'
import * as Inter from './inter.d'


// 請求傳入reqData參數   返回promise對象 由於全局請求我每次返回的類型都是不同的,因此我直接any
const doRequestAction = (reqData: Inter.Request): Promise<any> => {
  // 將不存在的參數字段使用默認值進行替換
  let req: Inter.RequestBase = { ...NormalRquestData, ...reqData }
  return new Promise((resolve, reject) => {
    //檢測是否開啓loading層 是否打開msak
    if (req.loading) Taro.showLoading({ title: req.title, mask: req.mask })
    Taro.request({
      url: req.url, //引入個人接口是特殊聲明的,因此我就不檢測http/https了
      method: req.method,
      data: req.data,
      header: req.header
    })
    .then(res => {
      // 大多數請求中 success並不表明成功,須要咱們本身檢測statusCode來確保
      if (res.statusCode === 200) {
        resolve(res.data) // 成功
      } else {
        // 若是失敗 檢測是否直接提示信息
        if(req.failToast) Taro.showToast({ title: '網絡很差,請求失敗!' })
        reject(res) // 失敗
      }
    })
    .catch(err => {
      // 若是失敗 檢測是否直接提示信息
      if (req.failToast) Taro.showToast({ title: '網絡很差,請求失敗!' })
      reject(err)
    })
    .finally(() => {
      // 請求結束 關閉loading層
      if (req.loading) Taro.hideLoading()
    })
  })
}
複製代碼

ok,請求方法寫到這裏咱們暫時只能告一段落了

6.完善api.ts,聲明全局域名以及每一個接口的定義.

//定義全局請求地址,由於咱們用到的地址是https://cnodejs.org/api/v1
export const DOMAIN: string = 'https://cnodejs.org/'
// 聲明獲取主題首頁接口地址並導出
export const topics: string = DOMAIN + 'api/v1/topics'
複製代碼

7.觀察api,聲明請求data文件,以及使用請求並返回promise以及返回類型

返回類型:cnodejs.org/api/v1/topi… 本身觀察,我就不截圖了。

// 請求主題接口參數類型
export interface TOPICSDATA {
  page: number,
  tab: 'ask' | 'share' | 'job' | 'good',
  limit: number,
  mdrender?: boolean
}
// 獲取主題的接口
export interface TOPICS {
  id: string,
  author_id: string,
  tab: string,
  content: string,
  title: string,
  last_reply_at: string,
  good: boolean,
  top: boolean,
  reply_count: number,
  visit_count: number,
  create_at: string,
  author: TOPICSAUTHOR
}
// 做者的類型
export interface TOPICSAUTHOR {
  loginname: string,
  avatar_url: string
}
複製代碼

8.自定義獲取方法,結合本身的請求方法返回新的promise對象

// 調用封裝方法 返回promise對象 獲得獲取到的數據
const getTopics = (data: Inter.TOPICSDATA): Promise<Inter.TOPICS> => {
  return doRequestAction({
    url: Api.topics,
    data: data
  })
}
複製代碼

9. 頁面內能夠調用getTopics方法拿到咱們的數據

import { getTopics } from '../../utils/request/request'
import { TOPICSDATA } from '../../utils/request/inter'

useEffect(()=>{
    let data: TOPICSDATA= {
      page: 1,
      tab: 'ask',
      limit: 10
    }
    getTopics(data).then(res=>console.log(res))
  },[])
複製代碼

topics數據.png

至此,一個簡易的封裝就完美的結束了,可是在實際開發中爲了本身的便利,咱們會封裝不少常用到的參數,這裏只是提供一個封裝思惟,具體還須要在你們的項目中去思考怎麼才能去優化代碼。

10.總結

知識點

  • es6語法: ...展開運算, 解構賦值, promise
  • ts:類型聲明, 可選項, type , any類型 ,單個類型定義
  • 封裝思惟,文件拆分

代碼鏈接:pan.baidu.com/s/1DZ9u7U1f… 密碼:t7rc

最後若是這篇文章對您有用,麻煩您點亮一個贊,感激涕零,若是代碼中有好的意見或建議,還望不吝賜教。

相關文章
相關標籤/搜索