重击是:
本自述文件是一项正在进行中的工作。您也可以在 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 的启发。谢谢马特!
获得麻省理工学院许可
感谢这些优秀的人(表情符号键):
多纳冯·韦斯特 ? | 杰里米·泰斯 | 尤赖马·埃斯特维兹 | 杰里米·巴尔加 | 布鲁克·斯嘉丽·亚洛夫 | 卡尔·霍基 | 浩二 |
汤姆·拜勒 | 伊恩·萨瑟兰 | 布莱克·约德 | 瑞安·欣奇 | 米罗·多伊基奇 | 桑蒂切维奇 |
该项目遵循所有贡献者规范。欢迎任何形式的贡献!