無論是app還是小程序或者其他端,交互中請求無處不在。
一個優(yōu)秀的封裝類,能讓你的開發(fā)效率事半功倍,所以封裝邏輯至關(guān)重要,當然我也是個小菜鳥,跟著自己的思路寫過一些封裝方法,一方面是想不足之處還望路過的大神給予指正,二者是為新手打開一個善于封裝思維的大門,下面進入到前置知識。
Api: cnodejs.org/api/v1
Ts: 簡易的類型聲明、接口等
Es6:結(jié)構(gòu)賦值、擴展運算、promise、export等
Taro:類react,以及小程序基礎(chǔ)知識等
import Taro from '@tarojs/taro' //回調(diào)調(diào)用 function doRequestAction(){ Taro.request({ url: '', data: {}, header: {}, success: res => {}, fail: err => {}, complete: info => {} }) } // promise調(diào)用 function doRequestAction(){ Taro.request({ url: '', data: {}, header: {} }) .then(res=>{}) .catch(err=>{}) .finally(()=>{}) } 復(fù)制代碼
其中利弊不多做介紹,我們將會用到promise的請求方式。
文件名 | 作用 |
---|---|
api.ts | 存放接口地址、以及統(tǒng)一請求域名前綴 |
request.ts | 封裝公共請求方法、以及調(diào)用方法集合 |
inter.d.ts | ts的聲明文件,主要存放返回值類型,請求參數(shù)類型等 |
我這里盡量寫es6寫法讓大家在今后的項目開發(fā)中更加順暢的使用,當然在自己的項目中請結(jié)合實際情況使用,不要盲目的接入一些新的寫法。下面進入知識點梳理:
import Taro from '@tarojs/taro' // 暫時考慮 req的屬性都會傳入 const doRequestAction = (req) => { return new Promise((resolve, reject) => { if (req.loading) Taro.showLoading({ title: req.title ? req.title : '數(shù)據(jù)加載中...' }) 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() }) }) } 復(fù)制代碼
1.將使用到的參數(shù)進行分離 2.每個參數(shù)給出默認值,如果不傳人將用默認值代替 3.使用ts聲明參數(shù)類型
export interface RequestBase { url: string, //字符串 method: 'POST' | 'GET', //常規(guī)請求方式,根據(jù)項目要求添加 data: any, // 每次的參數(shù)都是不固定的,因此我們暫時不聲明數(shù)據(jù)類型 header: RequestHeader, // 下面的requestheader類型, loading: boolean, // 請求是否開啟loading層 mask: boolean, //開啟loading層的情況下是否不能點擊,全屏遮罩 title: string, //開啟loading層的提示內(nèi)容 failToast: boolean //如果請求是否,我是否直接彈出我的提示 } export interface RequestHeader { 'content-type': string // 表示content-type類型必須聲明 } 復(fù)制代碼
上面的header,我重新定義了一個接口來聲明類型,是為了更方便的去管理這個數(shù)據(jù),試想如果我們平時需要將用戶的token帶入到header里面,那么我們就會在RequestHeader中在聲明一個token字段。
所謂的接口也就相當于我們這個數(shù)據(jù)里面有什么字段,字段是什么類型。
所以,我們在header中在加入token字段,實際項目中,可能還會帶入加密串,時間,以及其他的輔助驗證字段,這里只為了大家方便開發(fā)做出示例,實際還需看項目聲明。
特殊提醒:ts是可以不加逗號的
export interface RequestHeader { 'content-type': string // 表示content-type類型必須聲明 token: string } 復(fù)制代碼
聲明我們的默認參數(shù),在用戶沒有參數(shù)傳入的情況下,將會使用我們的默認參數(shù)來輔助請求。
// 使用默認參數(shù),當數(shù)據(jù)不傳入指定字段時替代 const NormalRquestData: RequestBase = { url: api.DOMAIN, // 默認請求地址 method: 'GET', // 默認get請求 header : { // 默認使用的header頭 "content-type": 'application/x-www-form-urlencoded', token: '' }, data: {}, // 默認沒有參數(shù),傳入空對象 loading: true, //默認開啟loading層 mask: true, //請求時不需要點擊 title: '數(shù)據(jù)加載中', //loading提示文字 failToast: false // 一般我們會處理相應(yīng)業(yè)務(wù)邏輯,就不直接提示阻斷流程 } 復(fù)制代碼
思考問題,我們?nèi)绻看蝿討B(tài) 帶上我們token? 用戶剛開始進入小程序,這時可能是沒有授權(quán),后面授權(quán)了我們要及時更新token來達到用戶交互目的 這時我們可以將header提出,當然我一般會在我的狀態(tài)管理中操作,這里做個例子為大家提供思路。
// 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' } } 復(fù)制代碼
思考問題:RequestBase參數(shù)都是必傳,但是我們的請求的時候參數(shù)都是可傳,如果我們將其都改為可選參數(shù),這個時候如果我們使用req.title(loading標題)、req.url('請求api地址') 在ts的檢測下,這些參數(shù)都是可能不存在的,這樣我們會寫大量判斷,那么我們的代碼就會變得相當糟糕~!因此我們再聲明一個接口用來可選參數(shù)的規(guī)范。
// 遍歷RequestBase中所有key,都改為可選參,這樣我們就只管維護RequestBase type Request = { [K in keyof RequestBase]?: RequestBase[K] } 復(fù)制代碼
import Taro from '@tarojs/taro' import * as Api from './api' import * as Inter from './inter.d' // 請求傳入reqData參數(shù) 返回promise對象 因為全局請求我每次返回的類型都是不一樣的,所以我直接any const doRequestAction = (reqData: Inter.Request): Promise<any> => { // 將不存在的參數(shù)字段使用默認值進行替換 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 => { // 大多數(shù)請求中 success并不代表成功,需要我們自己檢測statusCode來確保 if (res.statusCode === 200) { resolve(res.data) // 成功 } else { // 如果失敗 檢測是否直接提示信息 if(req.failToast) Taro.showToast({ title: '網(wǎng)絡(luò)不好,請求失??!' }) reject(res) // 失敗 } }) .catch(err => { // 如果失敗 檢測是否直接提示信息 if (req.failToast) Taro.showToast({ title: '網(wǎng)絡(luò)不好,請求失??!' }) reject(err) }) .finally(() => { // 請求結(jié)束 關(guān)閉loading層 if (req.loading) Taro.hideLoading() }) }) } 復(fù)制代碼
ok,請求方法寫到這里我們暫時只能告一段落了
//定義全局請求地址,因為我們用到的地址是https://cnodejs.org/api/v1 export const DOMAIN: string = 'https://cnodejs.org/' // 聲明獲取主題首頁接口地址并導(dǎo)出 export const topics: string = DOMAIN + 'api/v1/topics' 復(fù)制代碼
7.觀察api,聲明請求data文件,以及使用請求并返回promise以及返回類型 返回類型: cnodejs.org/api/v1/topi… 自己觀察我就不截圖了。
// 請求主題接口參數(shù)類型 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 } 復(fù)制代碼
// 調(diào)用封裝方法 返回promise對象 得到獲取到的數(shù)據(jù) const getTopics = (data: Inter.TOPICSDATA): Promise<Inter.TOPICS> => { return doRequestAction({ url: Api.topics, data: data }) } 復(fù)制代碼
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)) },[]) 復(fù)制代碼
至此,一個簡易的封裝就完美的結(jié)束了,但是在實際開發(fā)中為了自己的便利,我們會封裝很多經(jīng)常使用到的參數(shù),這里只是提供一個封裝思維,具體還需要在大家的項目中去思考怎么才能去優(yōu)化代碼。
知識點
無論是app還是小程序或者其他端,交互中請求無處不在。
一個優(yōu)秀的封裝類,能讓你的開發(fā)效率事半功倍,所以封裝邏輯至關(guān)重要,當然我也是個小菜鳥,跟著自己的思路寫過一些封裝方法,一方面是想不足之處還望路過的大神給予指正,二者是為新手打開一個善于封裝思維的大門,下面進入到前置知識。
Api: cnodejs.org/api/v1
Ts: 簡易的類型聲明、接口等
Es6:結(jié)構(gòu)賦值、擴展運算、promise、export等
Taro:類react,以及小程序基礎(chǔ)知識等
import Taro from '@tarojs/taro'
//回調(diào)調(diào)用
function doRequestAction(){
Taro.request({
url: '',
data: {},
header: {},
success: res => {},
fail: err => {},
complete: info => {}
})
}
// promise調(diào)用
function doRequestAction(){
Taro.request({
url: '',
data: {},
header: {}
})
.then(res=>{})
.catch(err=>{})
.finally(()=>{})
}
復(fù)制代碼
其中利弊不多做介紹,我們將會用到promise的請求方式。
文件名 | 作用 |
---|---|
api.ts | 存放接口地址、以及統(tǒng)一請求域名前綴 |
request.ts | 封裝公共請求方法、以及調(diào)用方法集合 |
inter.d.ts | ts的聲明文件,主要存放返回值類型,請求參數(shù)類型等 |
我這里盡量寫es6寫法讓大家在今后的項目開發(fā)中更加順暢的使用,當然在自己的項目中請結(jié)合實際情況使用,不要盲目的接入一些新的寫法。下面進入知識點梳理:
import Taro from '@tarojs/taro'
// 暫時考慮 req的屬性都會傳入
const doRequestAction = (req) => {
return new Promise((resolve, reject) => {
if (req.loading) Taro.showLoading({ title: req.title ? req.title : '數(shù)據(jù)加載中...' })
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()
})
})
}
復(fù)制代碼
1.將使用到的參數(shù)進行分離 2.每個參數(shù)給出默認值,如果不傳人將用默認值代替 3.使用ts聲明參數(shù)類型
export interface RequestBase {
url: string, //字符串
method: 'POST' | 'GET', //常規(guī)請求方式,根據(jù)項目要求添加
data: any, // 每次的參數(shù)都是不固定的,因此我們暫時不聲明數(shù)據(jù)類型
header: RequestHeader, // 下面的requestheader類型,
loading: boolean, // 請求是否開啟loading層
mask: boolean, //開啟loading層的情況下是否不能點擊,全屏遮罩
title: string, //開啟loading層的提示內(nèi)容
failToast: boolean //如果請求是否,我是否直接彈出我的提示
}
export interface RequestHeader {
'content-type': string // 表示content-type類型必須聲明
}
復(fù)制代碼
上面的header,我重新定義了一個接口來聲明類型,是為了更方便的去管理這個數(shù)據(jù),試想如果我們平時需要將用戶的token帶入到header里面,那么我們就會在RequestHeader中在聲明一個token字段。
所謂的接口也就相當于我們這個數(shù)據(jù)里面有什么字段,字段是什么類型。
所以,我們在header中在加入token字段,實際項目中,可能還會帶入加密串,時間,以及其他的輔助驗證字段,這里只為了大家方便開發(fā)做出示例,實際還需看項目聲明。
特殊提醒:ts是可以不加逗號的
export interface RequestHeader {
'content-type': string // 表示content-type類型必須聲明
token: string
}
復(fù)制代碼
聲明我們的默認參數(shù),在用戶沒有參數(shù)傳入的情況下,將會使用我們的默認參數(shù)來輔助請求。
// 使用默認參數(shù),當數(shù)據(jù)不傳入指定字段時替代
const NormalRquestData: RequestBase = {
url: api.DOMAIN, // 默認請求地址
method: 'GET', // 默認get請求
header : { // 默認使用的header頭
"content-type": 'application/x-www-form-urlencoded',
token: ''
},
data: {}, // 默認沒有參數(shù),傳入空對象
loading: true, //默認開啟loading層
mask: true, //請求時不需要點擊
title: '數(shù)據(jù)加載中', //loading提示文字
failToast: false // 一般我們會處理相應(yīng)業(yè)務(wù)邏輯,就不直接提示阻斷流程
}
復(fù)制代碼
思考問題,我們?nèi)绻看蝿討B(tài) 帶上我們token? 用戶剛開始進入小程序,這時可能是沒有授權(quán),后面授權(quán)了我們要及時更新token來達到用戶交互目的 這時我們可以將header提出,當然我一般會在我的狀態(tài)管理中操作,這里做個例子為大家提供思路。
// 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'
}
}
復(fù)制代碼
思考問題:RequestBase參數(shù)都是必傳,但是我們的請求的時候參數(shù)都是可傳,如果我們將其都改為可選參數(shù),這個時候如果我們使用req.title(loading標題)、req.url('請求api地址') 在ts的檢測下,這些參數(shù)都是可能不存在的,這樣我們會寫大量判斷,那么我們的代碼就會變得相當糟糕~!因此我們再聲明一個接口用來可選參數(shù)的規(guī)范。
// 遍歷RequestBase中所有key,都改為可選參,這樣我們就只管維護RequestBase
type Request = {
[K in keyof RequestBase]?: RequestBase[K]
}
復(fù)制代碼
import Taro from '@tarojs/taro'
import * as Api from './api'
import * as Inter from './inter.d'
// 請求傳入reqData參數(shù) 返回promise對象 因為全局請求我每次返回的類型都是不一樣的,所以我直接any
const doRequestAction = (reqData: Inter.Request): Promise<any> => {
// 將不存在的參數(shù)字段使用默認值進行替換
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 => {
// 大多數(shù)請求中 success并不代表成功,需要我們自己檢測statusCode來確保
if (res.statusCode === 200) {
resolve(res.data) // 成功
} else {
// 如果失敗 檢測是否直接提示信息
if(req.failToast) Taro.showToast({ title: '網(wǎng)絡(luò)不好,請求失?。?#39; })
reject(res) // 失敗
}
})
.catch(err => {
// 如果失敗 檢測是否直接提示信息
if (req.failToast) Taro.showToast({ title: '網(wǎng)絡(luò)不好,請求失敗!' })
reject(err)
})
.finally(() => {
// 請求結(jié)束 關(guān)閉loading層
if (req.loading) Taro.hideLoading()
})
})
}
復(fù)制代碼
ok,請求方法寫到這里我們暫時只能告一段落了
//定義全局請求地址,因為我們用到的地址是https://cnodejs.org/api/v1
export const DOMAIN: string = 'https://cnodejs.org/'
// 聲明獲取主題首頁接口地址并導(dǎo)出
export const topics: string = DOMAIN + 'api/v1/topics'
復(fù)制代碼
7.觀察api,聲明請求data文件,以及使用請求并返回promise以及返回類型 返回類型: cnodejs.org/api/v1/topi… 自己觀察我就不截圖了。
// 請求主題接口參數(shù)類型
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
}
復(fù)制代碼
// 調(diào)用封裝方法 返回promise對象 得到獲取到的數(shù)據(jù)
const getTopics = (data: Inter.TOPICSDATA): Promise<Inter.TOPICS> => {
return doRequestAction({
url: Api.topics,
data: data
})
}
復(fù)制代碼
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))
},[])
復(fù)制代碼
至此,一個簡易的封裝就完美的結(jié)束了,但是在實際開發(fā)中為了自己的便利,我們會封裝很多經(jīng)常使用到的參數(shù),這里只是提供一個封裝思維,具體還需要在大家的項目中去思考怎么才能去優(yōu)化代碼。
知識點
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)