重擊是:
本自述文件是一項正在進行中的工作。您也可以在 Twitter 上向我提問。
$ npm i thwack
或者
$ yarn add thwack
Axios 當年發佈時非常棒。它為我們提供了一個基於XMLHttpRequest
的基於承諾的包裝器,但該包裝器很難使用。但那是很久以前的事了,時代已經改變了——瀏覽器變得更聰明。也許您的數據獲取解決方案是時候跟上步伐了?
Thwack 是從頭開始建立的,考慮到了現代瀏覽器。正因為如此,它沒有 Axios 所擁有的包袱。 Axios gzip 後的重量約為 5k。另一方面,Thwack 則纖細約 1.5k。
它們支援相同的 API,但存在一些差異(主要圍繞options
),但在大多數情況下,它們應該能夠在許多應用程式中互換使用。
Thwack 並不像 Axios 那樣嘗試解決所有問題,而是提供了 98% 用戶真正需要的解決方案。這就是 Thrwack 足跡輕如羽毛的原因。
刮掉那個。 Thwack 提供與 Axios 相同等級的功能,但佔地面積小得多。而且Thwack 基於承諾的事件系統更容易使用。
以下方法適用於所有 Thrwack 實例。
thwack(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.request(options: ThwackOptions): Promise<ThwackResponse>
thwack.get(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.delete(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.head(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.post(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.put(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.patch(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.create(options: ThwackOptions): ThwackInstance;
create
方法使用給定的options
來建立(da!)目前 Thwack 實例的新子實例。
thwack.getUri(options: ThwackOptions): string;
Thrwacks URL 解析符合 RFC-3986 標準。 axios的不是。它由@thwack/resolve
提供支援。
Thrwack 支援以下事件類型: request
、 response
、 data
和error
。
有關 Thwack 事件系統的更多信息,請參閱下面的 Thwack 事件。
thwack.addEventListener(type: string, callback: (event:ThwackEvent) => Promise<any> ): void;
thwack.removeEventListener(type: string, callback: (event:ThwackEvent) => Promise<any> ): void;
Thwack 具有以下用於發出同時請求的輔助函數。它們主要是為了相容於 Axios。有關用法範例,請參閱下面的“操作方法”部分。
thwack.all(Promise<ThwackResponse>[])
thwack.spread(callback<results>)
options
參數具有下列屬性。
url
這是完全限定的 URL 或相對 URL。
baseURL
定義一個基本 URL,用於從上面的url
建立完全限定的 URL。必須是絕對 URL 或undefined
。如果在瀏覽器中執行或在 Node 或 React Native 上undefined
則預設為目前網頁的origin
+ pathname
。
例如,如果您這樣做:
thwack ( 'foo' , {
baseURL : 'http://example.com' ,
} ) ;
取得的 URL 將是:
http://example.com/foo
method
包含下列 HTTP 方法之一的字串: get
、 post
、 put
、 patch
、 delete
或head
。
data
如果method
是post
、 put
或patch
,則這是將用於建立請求正文的資料。
headers
您可以在此處放置任何可選的 HTTP 請求標頭。您在此處指定的任何標頭都將與任何實例標頭值合併。
例如,如果我們像這樣設定一個 Thwack 實例:
const api = thwack . create ( {
headers : {
'x-app-name' : 'My Awesome App' ,
} ,
} ) ;
然後,當您使用該實例時,您可以進行以下呼叫:
const { data } = await api . get ( 'foo' , {
headers : {
'some-other-header' : 'My Awesome App' ,
} ,
} ) ;
將發送的標頭是:
x-app-name: My Awesome App
some-other-header': 'My Awesome App'
defaults
這允許您讀取/設定此實例以及實際上任何子實例的預設選項。
例子:
thwack . defaults . baseURL = 'https://example.com/api' ;
例如, defaults
與傳遞給create
物件相同。例如,以下將輸出“https://example.com/api”。
const instance = thwack . create ( {
baseURL : 'https://example.com/api' ,
} ) ;
console . log ( instance . defaults . baseURL ) ;
另請注意,在實例上設定defaults
(甚至將options
傳遞給實例)不會影響父實例。因此,對於以下範例, thwack.defaults.baseURL
仍將是「https://api1.example.net/」。
thwack . defaults . baseURL = 'https://api1.example.net/' ;
const instance = thwack . create ( ) ;
instance . defaults . baseURL = 'https://example.com/api' ;
console . log ( thwack . defaults . baseURL ) ;
params
這是一個可選對象,包含將用於建立獲取 URL 的鍵/值對。如果baseURL
或url
中有任何:key
段,它們將被替換為匹配鍵的值。例如,如果您這樣做:
thwack ( 'orders/:id' , {
params : { id : 123 } ,
baseURL : 'http://example.com' ,
} ) ;
取得的 URL 將是:
http://example.com/orders/123
如果您沒有指定:name
,或param
數量多於:name
數量,則剩餘的鍵/值將設定為搜尋參數(即?key=value
)。
maxDepth
在 Thwack 引發錯誤之前,callbck 中可以發出的最大遞歸請求等級。這用於防止事件回調導致遞歸循環,如果它在沒有適當保護措施的情況下發出另一個request
。預設 = 3。
responseType
預設情況下,Thwack 會根據回應頭content-type
的值自動決定如何解碼回應資料。但是,如果伺服器回應的值不正確,您可以透過設定responseType
來覆蓋解析器。有效值為arraybuffer
、 document
(即formdata
)、 json
、 text
、 stream
和blob
。預設為自動。
Thwack 傳回的內容由下表決定。 「取得方法」列是data
中解析的內容。如果您沒有指定responseType
,Thwack將根據content-type
和responseParserMap
表自動決定獲取方法(請參閱下文)。
內容類型 | responseType | fetch 方法 |
---|---|---|
application/json | json | response.json() |
multipart/form-data | formdata | response.formData() |
text/event-stream | stream | 將response.body 當作data 傳回而不處理 |
blob | response.blob() | |
arraybuffer | response.arrayBuffer() | |
*/* | text | response.text() |
注意:由於#27741,React Native 目前不支援
stream
responseParserMap
決定使用哪個回應解析器的另一個有用方法是使用responseParserMap
。它允許您在回應標頭產生的content-type
和解析器類型之間設定映射。
Thwack 使用以下映射作為預設值,它允許json
和formdata
解碼。如果沒有匹配項,響應解析器預設為text
。您可以透過設定特殊的*/*
鍵來指定預設值。
{
"application/json" : " json " ,
"multipart/form-data" : " formdata " ,
"*/*" : " text "
} ;
您在responseParserMap
中指定的任何值都會合併到預設對映中。也就是說,您可以覆寫預設值和/或新增值。
舉例來說,您想將圖像下載到 blob 中。您可以將baseURL
設定為您的API端點和responseParserMap
,它將下載任何類型的映像為blob,但仍允許json
下載(因為這是content-type: application/json
)。
import thwack from 'thwack' ;
thwack . defaults . responseParserMap = { 'image/*' : 'blob' } ;
使用image/*
內容類型(例如image/jpeg
、 image/png
等)下載的任何 URL 都會使用blob
解析器進行解析。
const getBlobUrl = async ( url ) => {
const blob = ( await thwack . get ( url ) ) . data ;
const objectURL = URL . createObjectURL ( blob ) ;
return objectURL ;
} ;
請參閱在 CodeSandbox 上運行的範例。
請注意,您可以將此技術用於圖像以外的其他事物。
如您所看到的,使用responseParserMap
是消除為不同Thwack 呼叫設定responseType
需要的好方法。
validateStatus
此可選函數用於確定 Thwack 使用哪些狀態代碼傳回 Promise 或 throw。已通過響應status
。如果此函數傳回 true,則承諾已解決,否則承諾會被拒絕。
預設函數會拋出任何不在 2xx(即 200-299)範圍內的狀態
paramsSerializer
這是一個可選函數,Thwack 將呼叫它來序列化params
。例如,給定一個物件{a:1, b:2, foo: 'bar'}
,它應該序列化為字串a=1&b=2&foo=bar
。
對於大多數人來說,預設的序列化器應該可以正常工作。這主要是為了邊緣情況和 Axios 相容性。
請注意,預設序列化程序按字母順序排列參數,這是一個值得遵循的好習慣。但是,如果這不適合您的情況,您可以推出自己的序列化器。
resolver
您可以提供此函數來覆寫預設解析器行為。 resolver
有兩個參數:一個url
和一個必須未定義的baseURL
,或一個絕對 URL。您應該沒有什麼理由更換解析器,但這是您的逃生口,以防萬一您需要。
status
表示收到的 3 位 HTTP 狀態碼的number
。
ok
設定為 true 的boolean
是 2xx 範圍內的status
代碼(即成功)。該值不受validateStatus
影響。
statusText
表示status
代碼文字的string
。您應該在任何程式邏輯中使用status
代碼(或ok
)。
headers
帶有傳回的 HTTP 標頭的鍵/值物件。任何重複的標頭都會連接成一個標頭,並以分號分隔。
data
這將在串流和轉換後保存 HTTP 回應的返回正文。唯一的例外是如果您使用了stream
的responseType
,在這種情況下data
將直接設定到body
元素。
如果拋出ThwackResponseError
, data
將是回應正文的純文字表示形式。
options
處理請求的完整options
物件。此options
將與任何父實例以及defaults
完全合併。
response
由fetch
傳回的完整 HTTP Response
物件或來自合成事件回呼的response
。
如果 Thwack 請求的回應導致非 2xx status
碼(例如 404 Not Found),則拋出ThwackResponseError
。
注意:可能會引發其他類型的錯誤(例如錯誤的事件偵聽器回調),因此最佳實踐是詢問捕獲的錯誤以查看其是否屬於
ThwackResponseError
類型。
try {
const { data } = await thwack . get ( someUrl )
} catch ( ex ) {
if ( ex instanceof thwack . ThwackResponseError )
const { status , message } = ex ;
console . log ( `Thwack status ${ status } : ${ message } ` ) ;
} else {
throw ex ; // If not, rethrow the error
}
}
ThwackResponseError
具有普通 JavaScript Error
的所有屬性以及與成功狀態具有相同屬性的thwackResponse
屬性。
在Thwack 中建立的實例是基於父實例。父級的預設選項透過實例向下傳遞。這可以方便地在父級中設定可以影響子級的選項,例如baseURL
,
相反,父母可以使用addEventListener
來監視他們的孩子(有關範例,請參閱下面的如何記錄每個 API 呼叫)。
與實例結合,Thwack 事件系統使 Thwack 變得極其強大。有了它,您可以監聽不同的事件。
這是所有事件的事件流。正如您所看到的,如果您的回調盲目地發出request()
而不檢查它是否已經這樣做,您的程式碼可能會進入無限循環,所以要小心。
request
事件每當應用程式的任何部分呼叫其中一種資料擷取方法時,都會觸發request
事件。任何偵聽器都會獲得一個ThwackRequestEvent
對象,其中包含event.options
中的呼叫options
。這些事件偵聽器可以執行簡單的操作(記錄事件)或複雜的操作,例如阻止請求並使用(模擬資料)返回回應
// callback will be called for every request made in Thwack
thwack . addEventListener ( 'request' , callback ) ;
請注意,回調可以是
async
允許您推遲 Thwack,這樣您就可以在繼續之前出去並從不同的 URL 獲取資料。
response
事件該事件在收到 HTTP 標頭之後、傳輸和解析正文之前觸發。偵聽器將收到一個ThwackResponseEvent
對象,其中thwackResponse
鍵設定為響應。
data
事件該事件在正文被傳輸和解析後觸發。只有當提取返回 2xx 狀態代碼時才會觸發它。偵聽器將收到一個ThwackDataEvent
對象,其中thwackResponse
鍵設定為回應。
error
事件該事件在正文被傳輸和解析後觸發。如果提取返回非 2xx 狀態代碼,則會觸發它。偵聽器將收到一個ThwackErrorEvent
對象,其中thwackResponse
鍵設定為回應。
Thwack 將在 NodeJS 上運行,但需要window.fetch
的 polyfill。幸運的是,有一個很棒的 polyfill 可供您使用,稱為node-fetch
。
如果您使用的是 NodeJS 版本 10,您還需要Array#flat
和Object#fromEntries
的 polyfill。 NodeJS 版本 11+ 具有這些方法,且不需要 polyfill。
您可以自己提供這些polyfill,或使用以下方便的導入之一。如果您執行的是 NodeJS 11+,請使用:
import thwack from 'thwack/node' ; // NodeJS version 12+
如果您在 NodeJS 10 上運行,請使用:
import thwack from 'thwack/node10' ; // NodeJS version 10
如果您希望自己提供這些填充,那麼要使用 Thwack,您必須從thwack/core
匯入並將fetch
設定為fetch
的預設值。
import thwack from 'thwack/code' ;
thwack . defaults . fetch = global . fetch ;
這應該在您的應用程式啟動程式碼中完成,通常是index.js
。
注意:NodeJS 不支援
blob
的responseType
。
Thrwack 與 React Native 相容,不需要額外的 polyfill。請參閱下面的使用 React Native 編寫的範例應用程式。
注意:React Native 不支援
stream
,因為 #27741
您可以使用thwack.all()
和thwack.spread()
發出同時請求。然後,數據將作為一個陣列呈現給您的回調。
此處我們顯示兩個 GitHub 使用者的資訊。
function displayGitHubUsers ( ) {
return thwack
. all ( [
thwack . get ( 'https://api.github.com/users/donavon' ) ,
thwack . get ( 'https://api.github.com/users/revelcw' ) ,
] )
. then (
thwack . spread ( ( ... results ) => {
const output = results
. map (
( { data } ) => ` ${ data . login } has ${ data . public_repos } public repos`
)
. join ( 'n' ) ;
console . log ( output ) ;
} )
) ;
}
請注意,這些只是輔助函數。如果您使用async
/ await
您可以使用Promise.all
編寫此程式碼,而無需 Thwack 幫助程式。
async function displayGitHubUsers ( ) {
const results = await Promise . all ( [
thwack . get ( 'https://api.github.com/users/donavon' ) ,
thwack . get ( 'https://api.github.com/users/revelcw' ) ,
] ) ;
const output = results
. map ( ( { data } ) => ` ${ data . login } has ${ data . public_repos } public repos` )
. join ( 'n' ) ;
console . log ( output ) ;
}
您可以看到它在 CodeSandbox 中即時運行。
(演示受到 axios/fetch 上這篇 blob 帖子的啟發)
使用AbortController
透過在thwack
選項中傳遞其signal
來取消請求。
在瀏覽器中,您可以使用內建的AbortController。
import thwack from 'thwack' ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
thwack ( url , { signal } ) . then ( handleResponse ) . catch ( handleError ) ;
controller . abort ( ) ;
在 NodeJS 中,您可以使用類似 abort-controller 的東西。
import thwack from 'thwack' ;
import AbortController from 'abort-controller' ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
thwack ( url , { signal } ) . then ( handleResponse ) . catch ( handleError ) ;
controller . abort ( ) ;
如果您想對請求取消執行某些操作,您也可以監聽signal
上的abort
事件:
signal . addEventListener ( 'abort' , handleAbort ) ;
新增addEventListener('request', callback)
並將每個請求記錄到控制台。
import thwack from 'thwack' ;
thwack . addEventListener ( 'request' , ( event ) => {
console . log ( 'hitting URL' , thwack . getUri ( event . options ) ) ;
} ) ;
如果您使用 React,這裡有一個 Hook,您可以在您的應用程式中「使用」它來完成相同的事情。
import { useEffect } from 'react' ;
import thwack from 'thwack' ;
const logUrl = ( event ) => {
const { options } = event ;
const fullyQualifiedUrl = thwack . getUri ( options ) ;
console . log ( `hitting ${ fullyQualifiedUrl } ` ) ;
} ;
const useThwackLogger = ( ) => {
useEffect ( ( ) => {
thwack . addEventListener ( 'request' , logUrl ) ;
return ( ) => thwack . removeEventListener ( 'request' , logUrl ) ;
} , [ ] ) ;
} ;
export default useThwackLogger ;
這是有關如何使用它的程式碼片段。
const App = ( ) = {
useThwackLogger ( )
return (
< div >
...
</ div >
)
}
假設您有一個應用程式請求了一些用戶資料。如果應用程式存取特定 URL(例如users
)並查詢特定使用者 ID(例如123
),您希望阻止請求到達伺服器,而是模擬結果。
ThwackResponse
中的status
預設為 200,因此除非您需要模擬非 OK 回應,否則您只需要傳回data
。
thwack . addEventListener ( 'request' , async ( event ) => {
const { options } = event ;
if ( options . url === 'users' && options . params . id === 123 ) {
// tells Thwack to use the returned value instead of handling the event itself
event . preventDefault ( ) ;
// stop other listeners (if any) from further processing
event . stopPropagation ( ) ;
// because we called `preventDefault` above, the caller's request
// will be resolved to this `ThwackResponse` (defaults to status of 200 and ok)
return new thwack . ThwackResponse (
{
data : {
name : 'Fake Username' ,
email : '[email protected]' ,
} ,
} ,
options
) ;
}
} ) ;
通常需要將 DTO(資料傳輸物件)轉換為更易於客戶端使用的內容。在下面的範例中,我們將複雜的 DTO 轉換為firstName
、 lastName
、 avatar
和email
。從 API 呼叫返回但應用程式不需要的其他資料元素將被忽略。
您可以在此範例應用程式中查看 DTO 轉換、日誌記錄和傳回虛假資料的範例。
您可以在 CodeSandbox 上查看原始程式碼。
在此範例中,我們有一個 React Hook,它將圖像作為 Blob URL 載入。它將 URL 快取到會話儲存中的 Blob URL 映射。載入後,任何頁面刷新都會立即從 Blob URL 載入圖片。
const useBlobUrl = ( imageUrl ) => {
const [ objectURL , setObjectURL ] = useState ( '' ) ;
useEffect ( ( ) => {
let url = sessionStorage . getItem ( imageUrl ) ;
async function fetchData ( ) {
if ( ! url ) {
const { data } = await thwack . get ( imageUrl , {
responseType : 'blob' ,
} ) ;
url = URL . createObjectURL ( data ) ;
sessionStorage . setItem ( imageUrl , url ) ;
}
setObjectURL ( url ) ;
}
fetchData ( ) ;
} , [ imageUrl ] ) ;
return objectURL ;
} ;
請參閱 CodeSandbox 上的此範例
現在您在https://api.example.com
上有一個 REST 端點。假設您已將新的 REST 端點發佈到不同的 URL,並希望開始緩慢地將 2% 的網路流量路由到這些新伺服器。
注意:通常這將由後端的負載平衡器處理。此處顯示僅用於演示目的。
我們可以透過取代請求事件偵聽器中的options.url
來實現此目的,如下所示。
thwack . addEventListener ( 'request' , ( event ) => {
if ( Math . random ( ) >= 0.02 ) {
return ;
}
// the code will be executed for approximately 2% of the requests
const { options } = event ;
const oldUrl = thwack . getUri ( options ) ;
const url = new URL ( '' , oldUrl ) ;
url . origin = 'https://api2.example.com' ; // point the origin at the new servers
const newUrl = url . href ; // Get the fully qualified URL
event . options = { ... event . options , url : newUrl } ; // replace `options`]
} ) ;
與use-thwack
一起,為 React Native 編寫資料獲取應用程式再簡單不過了。
查看在 Expo 上運行的整個應用程式。
Thwack深受Axios 的啟發。謝謝馬特!
獲得麻省理工學院許可
感謝這些優秀的人(表情符號鍵):
多納馮·韋斯特 ? | 傑里米·泰斯 | 尤賴馬·埃斯特維茲 | 傑里米·巴爾加 | 布魯克·史嘉莉·亞洛夫 | 卡爾·霍基 | 浩二 |
湯姆·拜勒 | 伊恩·薩瑟蘭 | 布萊克·約德 | 瑞安·欣奇 | 米羅·多伊基奇 | 桑蒂切維奇 |
該項目遵循所有貢獻者規範。歡迎任何形式的貢獻!