
重擊是:
本自述文件是一項正在進行中的工作。您也可以在 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 的啟發。謝謝馬特!
獲得麻省理工學院許可
感謝這些優秀的人(表情符號鍵):
多納馮·韋斯特 ? | 傑里米·泰斯 | 尤賴馬·埃斯特維茲 | 傑里米·巴爾加 | 布魯克·史嘉莉·亞洛夫 | 卡爾·霍基 | 浩二 |
湯姆·拜勒 | 伊恩·薩瑟蘭 | 布萊克·約德 | 瑞安·欣奇 | 米羅·多伊基奇 | 桑蒂切維奇 |
該項目遵循所有貢獻者規範。歡迎任何形式的貢獻!