Skip to content

Commit

Permalink
feat: rufetch
Browse files Browse the repository at this point in the history
  • Loading branch information
renjiazheng committed Apr 22, 2020
0 parents commit f2f7ea3
Show file tree
Hide file tree
Showing 15 changed files with 7,176 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "react-app",
"rules": {}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
.cache
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
### intro

a lightweight fetch lib with middleware inspire by koa



### quick start

```
yarn add @jarzzzi/rufetch
```


### example

```code
const http = new Rufetch({
baseURL: 'https://github.com/api',
})
const getUser = (name) => {
return http.get(`/user/${name}`)
}
const getReps = (name, page) => {
return http.get(`/user/${name}`, {params: {page}})
}
const createReps = (name, data) => {
return http.post(`/user/${name}`, {data})
}
```

### middleware
- built in middleware
- cache
- fetch

```code
http.use(async (ctx: any, next: any) => {
await next()
const {error} = ctx.res
if (error?.status >= 400 && error?.status < 500) {
console.error('client error')
}
if (error?.status >= 500) {
console.error('server error')
}
if (error === 'fetch timeout') {
console.error('fetch timeout')
}
})
```

### api
- options
- baseURL
- timeout: default 15000
- params: url search params
- data: body alias
- responseType: default json, arrayBuffer, blob, json, text, formData, stream...
- cacheOptions
- cache: boolean
- expiredAge: default 5000
- body
- cache
- credentials
- headers
- integrity
- keepalive
- method
- mode
- redirect
- referrer
- referrerPolicy
- signal
- window
- new (options)
- instance.get
- instance.post
- instance.put
- instance.delete
- instance.head
- instance.use: add middleware
12 changes: 12 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./index.tsx"></script>
</body>
</html>
34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@jarzzzi/rufetch",
"version": "1.0.0",
"description": "a fetch lib with middleware inspire by koa",
"entry": "src/index.ts",
"module": "dist/esm/index.js",
"main": "dist/cjs/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"demo": "parcel serve ./demo/index.html",
"build": "rollup -c rollup.config.ts",
"version": "npm version patch",
"publish": "npm publish --access=public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Jarweb/rufetch.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/Jarweb/rufetch/issues"
},
"homepage": "https://github.com/Jarweb/rufetch#readme",
"devDependencies": {
"@jarzzzi/nidavel": "^1.0.6",
"rollup": "^2.6.1",
"typescript": "^3.8.3"
},
"dependencies": {
"isomorphic-fetch": "^2.2.1"
}
}
6 changes: 6 additions & 0 deletions rollup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import configRollup from '@jarzzzi/nidavel'

export default configRollup({
target: ['esm'],
input: './src/index.ts',
})
44 changes: 44 additions & 0 deletions src/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 默认过期时长,只有 http get、显式设置 cache 的数据请求才进行缓存
const EXPIRE_AGE = 5000
// 会打包出重复的 2份代码,每份代码初始化会重置 window._request_cache_
window._request_cache_ = window._request_cache_ || {}

export function getRequestCache(key: string) {
const now = Date.now()
// 过期
if (window._request_cache_[key] &&
window._request_cache_[key]?.status === 'success' &&
(window._request_cache_[key]?.createTime as number) + (window._request_cache_[key]?.expiredAge as number) <= now
) {
removeRequestCache(key)
}
return window._request_cache_[key]
}

export function setRequestCache(key: string, value: any) {
const now = Date.now()
// 未过期
if (window._request_cache_[key] &&
window._request_cache_[key]?.status === 'success' &&
(window._request_cache_[key]?.createTime as number) + (window._request_cache_[key]?.expiredAge as number) > now) return;

window._request_cache_[key] = {
expiredAge: window._request_cache_[key]?.expiredAge || EXPIRE_AGE,
status: 'success',
createTime: Date.now(),
value: value,
}
}

export function pendingRequestCache(key: string, expiredAge?: number) {
window._request_cache_[key] = {
status: 'pending',
createTime: Date.now(),
expiredAge: expiredAge || EXPIRE_AGE,
value: undefined,
}
}

export function removeRequestCache(key: string) {
window._request_cache_[key] = undefined
}
25 changes: 25 additions & 0 deletions src/compose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default function compose (middleware: any[]) {
let index = -1

return function (context: any, next: any) {
function dispatch (i: number): any {
if (i <= index) {
return Promise.reject(new Error(''))
}

index = i

let fn = middleware[i]
if (i === middleware.length) fn = next;
if (!fn) return Promise.resolve()

try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (error) {
return Promise.reject(error)
}
}

return dispatch(0)
}
}
170 changes: 170 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import compose from './compose'
import middlewareFetch from './middlewareFetch'
import middlewareCache from './middlewareCache'

interface RufetchOption {
baseURL?: string,
timeout?: number,
// like get...
params?: RequestInit['body'],
// like post
data?: RequestInit['body'],
// arrayBuffer, blob, json, text, formData, stream..., default json
responseType?: string,
// built in cache, not native request cache
cacheOptions?: {
cache: boolean,
expiredAge?: number,
},
// native request config
body?: RequestInit['body'],
cache?: RequestInit['cache'],
credentials?: RequestInit['credentials'],
headers?: RequestInit['headers'],
integrity?: RequestInit['integrity'],
keepalive?: RequestInit['keepalive'],
method?: RequestInit['method'],
mode?: RequestInit['mode'],
redirect?: RequestInit['redirect'],
referrer?: RequestInit['referrer'],
referrerPolicy?: RequestInit['referrerPolicy'],
signal?: RequestInit['signal'],
window?: RequestInit['window'],
}

export default class Rufetch {
private options: RufetchOption = {}
private middlewares: Function[] = []

constructor(options: RufetchOption) {
this.options = options
}

use = (middleware: any) => {
this.middlewares.push(middleware)
return this
}

preRequest = (method: string) => {
return ( url: string, rufetch?: RufetchOption) => {
const {
baseURL: defaultBaseURL,
timeout: defaultTimeout,
params: defaultParams,
data: defaultData,
responseType: defaultResponseType,
cacheOptions: defaultCacheOptions,
body: defaultBody,
cache: defaultCache,
credentials: defaultCredentials,
headers: defaultHeaders,
integrity: defaultIntegrity,
keepalive: defaultKeepalive,
method: defaultMethod,
mode: defaultMode,
redirect: defaultRedirect,
referrer: defaultReferrer,
referrerPolicy: defaultReferrerPolicy,
signal: defaultSignal,
window: defaultWindow,
...defaultRest
} = this.options

const {
baseURL,
timeout,
params,
data,
responseType,
cacheOptions,
body,
cache,
credentials,
headers,
integrity,
keepalive,
method,
mode,
redirect,
referrer,
referrerPolicy,
signal,
window,
...restOptions
} = rufetch || {}

const defaultRequestInit: RequestInit = {
body: defaultBody,
cache: defaultCache,
credentials: defaultCredentials,
headers: defaultHeaders,
integrity: defaultIntegrity,
keepalive: defaultKeepalive,
method: defaultMethod,
mode: defaultMode,
redirect: defaultRedirect,
referrer: defaultReferrer,
referrerPolicy: defaultReferrerPolicy,
signal: defaultSignal,
window: defaultWindow,
}
const requestInit: RequestInit = {
body,
cache,
credentials,
headers,
integrity,
keepalive,
method,
mode,
redirect,
referrer,
referrerPolicy,
signal,
window,
}
requestInit.method = method

const middlewares = [
...this.middlewares,
middlewareCache,
middlewareFetch
]
const ctx: any = {}

const rurl = url.indexOf(defaultBaseURL || '') > -1 ? url : defaultBaseURL + url
const reqInit = Object.assign({}, defaultRequestInit, requestInit)
const rbody = Object.assign({}, data, body)

reqInit.body = rbody ? rbody : defaultBody
reqInit.headers = Object.assign({}, defaultHeaders, headers)

ctx.req = {
url: rurl,
method,
timeout: timeout ? timeout : (defaultTimeout || 3000),
params: params ? params : defaultParams,
requestInit: reqInit,
...Object.assign({}, defaultRest, restOptions),
}
ctx.res = {}

return new Promise(async (resolve, reject) => {
try {
await compose(middlewares)(ctx, null)
resolve(ctx.res.data)
} catch (error) {
if (!error) return;
throw error
}
})
}
}

get = this.preRequest('get')
post = this.preRequest('post')
put = this.preRequest('put')
delete = this.preRequest('delete')
head = this.preRequest('head')
}

Loading

0 comments on commit f2f7ea3

Please sign in to comment.