Skip to content

Commit

Permalink
Release v1.3.0 (#3186)
Browse files Browse the repository at this point in the history
* fix: compatible with the index page

* fix: create histroy when render root app

* feat: support promise function with useRequest (#3188)

* fix: repository url (#3192)

* feat: optimize webpackbar (#3187)

* feat: optimize webpackbar

* fix: rename eslintLoaderOptions to eslint

* fix: upgrate react-dev-utils (#3190)

* fix: upgrate react-dev-utils

* v1.2.2-alpha.1

* fix: set transportMode to ws

Co-authored-by: ClarkXia <xiawenwu41@gmail.com>

* feat: support route wrappers (#3189)

* feat: support route wrappers

* feat: support pageConfig wrappers

* refactor: wrappers api

* docs: add wrappers api to ruotes

* fix: destructuring routes

* feat: enhance use request types (#3195)

* feat: enhance useRequest types

* chore: remove comments

* docs: useRequest usage

* v1.2.2-alpha.2

* docs: add withFullResponse api

* docs: refactor request docs

* chore: typo

* chore: useRequest docs

* docs: fix typo

* docs: optimize title

* v1.2.2-alpha.3

* Fix/release bugs (#3205)

* fix: types & log

* v1.2.2-alpha.4

* fix: no match single page file
  • Loading branch information
chenbin92 authored May 14, 2020
2 parents 3495c32 + dda52a4 commit 405f1c2
Show file tree
Hide file tree
Showing 41 changed files with 853 additions and 169 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const jsRules = deepmerge(eslint, {
const tsRules = deepmerge(tslint, {
rules: {
...commonRules,
"@typescript-eslint/ban-ts-ignore": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/interface-name-prefix": 0,
"@typescript-eslint/explicit-function-return-type": 0,
Expand Down
275 changes: 241 additions & 34 deletions docs/guide/basic/request.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,159 @@ title: 数据请求
order: 4
---

大多数前端应用都需要通过 HTTP 协议与后端服务器通讯。框架内置提供了请求功能,基于社区主流的 axios 进行封装,提供了 request 和 useRequest Hooks 方法。
大多数前端应用都需要通过 HTTP 协议与后端服务器通讯。在 icejs 框架中内置约定和规范了一套从 UI 交互到请求服务端数据的完整方案,更进一步简化了应用的数据请求流程,基于此提供了 request 和 useRequest Hooks 方法。

## 目录约定

目录组织如下:

```diff
src
├── models
+├── services // 定义全局数据请求
+│ └── user.ts
└── pages
| ├── Home
| │   ├── models
+| │   ├── services // 定义页面级数据请求
+| │   | └── repo.ts
| │   ├── components
| │   | └── Todo
| | │   | ├── index.tsx
| | │   | ├── model.ts
+| | │   | └── service.ts // 定义组件级数据请求
| │   └── index.tsx
| ├── About
| │   ├── services
| │   ├── components
| │   └── index.tsx
└── app.ts
```

## 定义 service

通过调用 request 定义数据请求如下:

```ts
import { request } from 'ice';

export default {
// 简单场景
async getUser() {
return await request('/api/user');
},

## request
// 参数场景
async getRepo(id) {
return await request(`/api/repo/${id}`);
},

// 格式化返回值
async getDetail(params) {
const data = await request({
url: `/api/detail`;
params,
});

return data.map(item => {
return {
...item,
price: item.oldPrice,
text: item.status === '1' ? '确定' : '取消'
};
});
}
}
```

## 消费 service

消费 service 主要有两种方式:

* 在模型中调用 service:`service` -> `model` -> `view`
* 在视图中调用 service:`service` -> `view`

### 在模型中调用 service

* `service`:约定数据请求统一管理在 services 目录下;
* `model`:约定数据请求统一在 models 里进行调用;
* `view`:最终在视图里通过调用 models 的 effects 的方法触发数据请求。

在模型中调用定义好的 service:

```ts
import userService from '@/services/user';

// src/models/user.ts
export default {
state: {
name: 'taoxiaobao',
age: 20
},
reducers: {
update(prevState, payload) {
return { ...prevState, ...payload };
}
},
effects: (dispatch) => {
async fetchUserInfo() {
const data = await userService.getUser();
dispatch.user.update(data);
}
}
}
```

* 在视图中调用模型方法:

```ts
import React, { useEffect } from 'react';
import { store } from 'ice';

const HomePage = () => {
// 调用定义的 user 模型
const [ userState, userDispatchers ] = store.useModel('user');

useEffect(() => {
// 调用 user 模型中的 fetchUserInfo 方法
userDispatchers.fetchUserInfo();
}, []);

return (
<>Home</>
);
}
```

### 在视图中调用 service

* `service`:约定数据请求统一管理在 services 目录下;
* `view`:最终在视图里通过 useRequest 直接调用 service 触发数据请求。

```ts
import React, { useEffect } from 'react';
import { useRequest } from 'ice';
import userService from '@/services/user';

export default function HomePage() {
// 调用 service
const { data, error, isLoading, request } = useRequest(userService.getUser);

useEffect(() => {
// 触发数据请求
request();
}, []);


return (
<>Home</>
);
}
```

## API

### request

request 基于 axios 进行封装,在使用上与 axios 保持一致,使用方式如下:

Expand All @@ -15,7 +165,7 @@ import { request } from 'ice'
async function getList() {
try {
const data = await request({
url: '/api/list'
url: '/api/user'
});
console.log(data);
} catch (error) {
Expand All @@ -24,8 +174,6 @@ async function getList() {
}
```

> 注意:request API 当前并未返回整个 response,如有需求可先在 issue 中反馈
常用使用方式:

```js
Expand All @@ -39,6 +187,7 @@ RequestConfig:

```js
{
// `url` is the server URL that will be used for the request
url: '/user',
// `method` is the request method to be used when making the request
method: 'get', // default
Expand All @@ -63,53 +212,108 @@ RequestConfig:
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `validateStatus` defines whether to resolve or reject the promise for a given
// HTTP response status code. If `validateStatus` returns `true` the promise will be resolved; otherwise, the promise will be rejected.
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// should be made return full response
withFullResponse: false,
}
```

更完整的配置请 [参考](https://github.com/axios/axios#request-config)

request 默认只返回服务端响应的数据,并未返回整个 response,如需返回可以设置 `withFullResponse` 属性,完整的 response 返回格式如下:

Response Schema:

```ts
{
// `data` is the response that was provided by the server
data: {},

// `status` is the HTTP status code from the server response
status: 200,

// `statusText` is the HTTP status message from the server response
statusText: 'OK',

// `headers` the HTTP headers that the server responded with
// All header names are lower cased and can be accessed using the bracket notation.
// Example: `response.headers['content-type']`
headers: {},

// `config` is the config that was provided to `axios` for the request
config: {},

// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance in the browser
request: {}
}
```

更完整的配置请参考:https://github.com/axios/axios#request-config
### useRequest

* 可以用在 Function Component,使用 useRequest 可以极大的简化对请求状态(error/loading)的管理。
* 可以接收一个 `Object` 或者是 `(...args)=> any` 作为参数。

## useRequest
#### 传入对象作为参数

useRequest 用在 Function Component,使用 useRequest 可以极大的简化对请求状态(错误/loading)的管理:
```ts
const { data, loading, error, request } = useRequest(options: RequestConfig);
```

```jsx
使用示例:

```ts
import { useRequest } from 'ice';

function ListView(props) {
const { data, loading, error, request } = useRequest({
url: '/api/list',
method: 'GET',
export default function HomePage() {
const { data, error, isLoading, request } = useRequest({
url: '/api/getRepo',
params: {
id: 123
}
});
const dataSource = data ? data.dataSource : [];

useEffect(() => {
// 实际请求配置会跟 useRequest 的参数合并
request({
params: { a: 1 }
});
request();
}, []);


return (
<>Home</>
);
}
```

#### 传入函数作为参数

```ts
const { data, loading, error, request } = useRequest((...args: any[]) => any);
```

使用示例:

```ts
// src/pages/Home/index.tsx
import { useRequest } from 'ice';
import services from '@/services';

export default function HomePage() {
const { data, error, isLoading, request } = useRequest(services.getRepo);

useEffect(() => {
// 动态传入参数
const id = 123;
request(id);
}, []);


return (
<>
{error && <div>{error.message}</div>}
{loading ? (
<div>loading....</div>
) : (
(dataSource || []).map(item => {
return <div>{item.name}</div>;
})
)}
</>
<>Home</>
);
}
```

## 请求配置
### 请求配置

在实际项目中通常需要对请求进行全局统一的封装,例如配置请求的 baseURL、统一 header、拦截请求和响应等等,这时只需要在应用的的 appConfig 中进行配置即可。

Expand All @@ -118,6 +322,9 @@ import { createApp } from 'ice';

const appConfig = {
request: {
// 可选的,全局设置 request 是否返回 response 对象,默认为 false
withFullResponse: false,

baseURL: '/api',
headers: {},
// ...RequestConfig 其他参数
Expand Down
3 changes: 3 additions & 0 deletions docs/guide/basic/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ icejs 提供了 **配置式路由** 和 **约定式路由** 两种方案,默
import UserLayout from '@/Layouts/UserLayout';
import UserLogin from '@/pages/UserLogin';
import NotFound from '@/components/NotFound';
import wrapperPage from '@/components/WrapperPage';

const routerConfig = [
// 分组路由,children 里的路由会将父节点的 component 作为布局组件
Expand All @@ -27,6 +28,8 @@ const routerConfig = [
path: '/login',
exact: true,
component: UserLogin,
// 配置路由的高阶组件
wrappers: [wrapperPage]
},
{
path: '/',
Expand Down
5 changes: 3 additions & 2 deletions examples/basic-request/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { createApp, IAppConfig, history } from 'ice';
import { createApp, IAppConfig } from 'ice';

const appConfig: IAppConfig = {
app: {
rootId: 'ice-container',
},
request: {
// 可选的,全局设置 request 是否返回 response 对象,默认为 false
// withFullResponse: true,
baseURL: '/api',
interceptors: {
response: {
// 可选的
onConfig: (config) => {
console.log({history});
return config;
},
// 可选的
Expand Down
Loading

0 comments on commit 405f1c2

Please sign in to comment.