Skip to content

Commit

Permalink
Feature/mock (#17)
Browse files Browse the repository at this point in the history
* docs: add mock feature

* feat: close #5, mock data
  • Loading branch information
BuptStEve authored Dec 9, 2018
1 parent dbbf0c8 commit 6733b38
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 10 deletions.
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module.exports = {
'installation',
'',
'middleware',
'mock',
'export-utils',
'../config/',
],
Expand Down
6 changes: 5 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ actionText: 快速上手 →
actionLink: /quick-start/
features:
- title: 支持多端
details: 支持 web 端和小程序端
details: 支持 web 端、Node 端和小程序端
- title: 支持跨域
details: 默认使用 axios,也支持降级为 jsonp
- title: 可配置
details: 可配置接口类型、请求方式、默认参数、必填参数等属性
- title: 支持 mock
details: 接口数据支持 mock,方便开发调试
- title: 中间件
details: koa 风格中间件,方便添加各种特技
footer: MIT Licensed | Copyright © 2018-present StEve Young
Expand Down
21 changes: 21 additions & 0 deletions docs/config/detail.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ export default {
}
```

## mock 模拟接口数据
* 类型:`Object``Function`
* 默认值:`{}`

模拟接口数据,可以直接填数据,或是填函数。函数将收到 `params` 参数对象,即最终发送给接口的数据对象。

```js
export default {
// 对象形式
mock: { code: 0, data: 'some data' },

// 函数形式
mock: (params) => ({
code: params.mockCode,
data: params.mockData,
}),
}
```

详情参阅 [快速上手 - mock 章节](../quick-start/mock.md)

## prefix 接口中间地址
建议与文件同名,方便维护。

Expand Down
6 changes: 4 additions & 2 deletions docs/config/own.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default {
}
```

有时接口地址较长,可以添加 `name` 配置重命名接口,这样就可以这样调用
有时接口地址较长或不方便直接调用,可以添加 `name` 配置重命名接口,这样就可以这样调用

```js
exampleApi.fooBar({ ... })
Expand Down Expand Up @@ -67,4 +67,6 @@ export default {
有时某个接口正好不需要上一级中 `commonParams` 的参数。那么可以传递 `null` 覆盖上一级中的 `commonParams`

## 其他参数
其他参数参阅上一节 [详细配置](./detail.md)
上一节中的所有参数(除了 `pathList` 外)均有效,且优先级最高。

详情参阅上一节 [详细配置](./detail.md)
178 changes: 178 additions & 0 deletions docs/quick-start/mock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# 数据 mock
## 静态配置
即将 mock 数据直接填在该接口的配置中。

### 简单对象
简单粗暴,填数据就完事儿了~

```js
{
pathList: [
// 以 foo 接口为例
{
path: 'foo',

// 对象形式
mock: { code: 0, data: 'some data' },
},
],
}
```

### mock 函数
使用函数形式,用法上会更灵活一些。

```js
{
pathList: [
// 以 foo 接口为例
{
path: 'foo',

// 函数形式
mock: (params) => ({
code: params.mockCode,
data: params.mockData,
}),
},
],
}
```

::: tip
`params` 即最终传入接口的参数对象。
:::

```js
import { exampleApi } from '@/apis/'

// 填写 mock 数据
const mockCode = 0
const mockData = { foo: 'bar' }

// 请求将收到 mock 数据
exampleApi.foo({ mockCode, mockData })
.then(({ code, data }) => {
console.log(code, data) // 0 {foo: "bar"}
})
```

### 多接口公共 mock
mock 属性不仅可以填在各个接口处,也可以将其放在上一级,mock 当前配置中的所有接口。

```js
{
// 公共 mock
mock: ({ __mockData__ }) => __mockData__,

pathList: [
// 自身的 mock 配置优先级更高
{ path: 'foo', mock: { code: 0 } },

// 没填自身 mock,则默认使用公共 mock
{ path: 'bar' },

// 禁用 mock
{ path: 'null', mock: null },
],
}
```

```js
import { exampleApi } from '@/apis/'

const __mockData__ = { code: 123 }

// 使用自己定义 mock 数据
exampleApi.foo({ __mockData__ })
.then(({ code }) => {
console.log(code) // 0
})

// 使用公共的 mock 数据
exampleApi.bar({ __mockData__ })
.then(({ code }) => {
console.log(code) // 123
})
```

更多配置优先级内容请参阅[配置说明](../config/)部分。

## 动态配置
即为每个导出的 `api` 函数添加 `mock` 属性,在业务侧用以下方式调用。

```js
import { exampleApi } from '@/apis/'

// 填写 mock 数据
exampleApi.foo.mock = {
code: 0,
data: { foo: 'bar' },
}

// 同样支持 mock 函数
exampleApi.foo.mock = () => ({
code: 0,
data: { foo: 'bar' },
})

// 请求将收到 mock 数据
exampleApi.foo().then(({ code, data }) => {
console.log(code, data) // 0 {foo: "bar"}
})
```

## 同时配置
### 优先级
若是同时配置了静态和动态 mock,动态配置的 mock 数据优先级更高。

::: tip
优先级:动态 > 静态
:::

* 接口配置
```js
{
pathList: [
{
path: 'foo',
mock: (params) => ({ code: params.mockCode }),
},
],
}
```

* 业务侧
```js
import { exampleApi } from '@/apis/'

// 动态配置的数据将覆盖静态配置的数据
exampleApi.foo.mock = { code: 1 }

exampleApi.foo({ mockCode: 0 })
.then(({ code }) => {
console.log(code) // 1
})
```

### 关闭 mock
可以通过以下代码实现关闭 mock 功能。

```js
import { exampleApi } from '@/apis/'

// 关闭 mock
exampleApi.foo.mock = null

// 即使传递 mock 数据也不起作用
exampleApi.foo({ mockCode: 404 })
.then(({ code }) => {
console.log(code) // 实际接口的返回值
})
```

::: tip
其实动态配置 `exampleApi.foo.mock` 的默认值就是静态配置的值,而在 `tua-api` 底层读取的就是 `exampleApi.foo.mock`

所以自然动态配置的优先级更高,并且赋值为 `null` 即可关闭 mock。
:::
1 change: 1 addition & 0 deletions examples/apis-mp/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ tuaApi.use(async (ctx, next) => {
// console.log('after: ', ctx)
})

export const mockApi = tuaApi.getApi(require('./mock').default)
export const fakeWxApi = tuaApi.getApi(require('./fake-wx').default)
24 changes: 24 additions & 0 deletions examples/apis-mp/mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default {
// 该参数表示请求的公用服务器地址。
host: 'http://example-base.com/',

// 该参数表示请求的中间路径,建议与文件同名,以便后期维护。
prefix: 'mock',

// 所有请求类型(可选值 OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT)
type: 'get',

// 公共 mock
mock: ({ __mockData__ }) => __mockData__,

pathList: [
// 自身的 mock 配置优先级更高
{ path: 'foo', mock: { code: 500 } },

// 没填自身 mock,则默认使用公共 mock
{ path: 'bar' },

// 禁用 mock
{ path: 'null', mock: null },
],
}
17 changes: 17 additions & 0 deletions examples/apis-web/fake-get.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,22 @@ export default {
path: 'no-afterFn-data',
afterFn: () => {},
},
/**
* mock-object-data
*/
{
name: 'mockObjectData',
path: 'mock-object-data',
mock: { code: 404, data: {} },
},
/**
* mock-function-data
*/
{
name: 'mockFnData',
path: 'mock-function-data',
reqType: 'axios',
mock: ({ mockCode }) => ({ code: mockCode, data: {} }),
},
],
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tua-api",
"version": "0.3.3",
"version": "0.4.0",
"main": "dist/TuaApi.umd.js",
"module": "dist/TuaApi.es.js",
"jsnext:main": "dist/TuaApi.es.js",
Expand Down
26 changes: 21 additions & 5 deletions src/TuaApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TuaApi {
/**
* @param {Object} options
* @param {String} options.host 服务器基础地址,例如 https://example.com/
* @param {String} options.reqType 请求类型
* @param {String} options.reqType 使用什么工具发(axios/jsonp/wx)
* @param {Function[]} options.middleware 中间件函数数组
* @param {Object} options.axiosOptions 透传 axios 配置参数
* @param {Object} options.jsonpOptions 透传 fetch-jsonp 配置参数
Expand Down Expand Up @@ -107,11 +107,21 @@ class TuaApi {
axiosOptions,
...rest
}) {
// check type
if (VALID_REQ_TYPES.indexOf(reqType) === -1) {
logger.error(`reqType 的有效值为: ${VALID_REQ_TYPES.join(', ')}!`)
throw Error('invalid reqType')
}

// mock data
if (rest.mock) {
const resData = typeof rest.mock === 'function'
? rest.mock(data)
: { ...rest.mock }

return Promise.resolve({ data: resData })
}

const method = type.toUpperCase()

if (reqType === 'wx') {
Expand Down Expand Up @@ -189,6 +199,7 @@ class TuaApi {
* 接受 api 对象,返回待接收参数的单个 api 函数的对象
* @param {Object} options
* @param {String} options.type 接口请求类型 get/post...
* @param {Object|Function} options.mock 模拟的响应数据或是生成数据的函数
* @param {String} options.name 自定义的接口名称
* @param {String} options.path 接口路径名称
* @param {String[]} options.params 接口参数数组
Expand All @@ -197,10 +208,15 @@ class TuaApi {
* @param {Function} options.beforeFn 在请求发起前执行的钩子函数(将被废弃)
* @param {Function[]} options.middleware 中间件函数数组
* @param {Boolean} options.useGlobalMiddleware 是否使用全局中间件
* @param {String} options.host 服务器地址
* @param {String} options.reqType 使用什么工具发
* @param {Object} options.axiosOptions 透传 axios 配置参数
* @param {Object} options.jsonpOptions 透传 fetch-jsonp 配置参数
* @return {Object} 以 apiName 为 key,请求函数为值的对象
*/
_getOneReqMap ({
type = 'get',
mock,
name,
path,
params = {},
Expand Down Expand Up @@ -241,14 +257,14 @@ class TuaApi {

// 请求的上下文信息
const ctx = {
req: { args, type, path, params, prefix, apiName, fullPath, callbackName, reqFnParams: {}, ...rest },
req: { args, type, path, params, prefix, apiName, fullPath, callbackName, reqFnParams: {}, mock: apiFn.mock, ...rest },
}

// 中间件函数
const middlewareFn = this._getMiddlewareFn(middleware, useGlobalMiddleware)

// 执行完 beforeFn 后执行的函数
const beforeFnCallback = (rArgs = {}) => {
const beforeFnCb = (rArgs = {}) => {
// 兼容小程序传递请求头(建议还是放在中间件中)
if (rArgs.header) {
ctx.req.reqFnParams.header = rArgs.header
Expand All @@ -268,7 +284,7 @@ class TuaApi {
}

return beforeFn()
.then(beforeFnCallback)
.then(beforeFnCb)
// 执行请求中间件函数
.then(() => middlewareFn(ctx))
// 请求执行完成后的钩子
Expand All @@ -280,8 +296,8 @@ class TuaApi {
)
}

// 将请求的 key 和 params 挂上去,以便在 ssr 时预取数据
apiFn.key = fullPath
apiFn.mock = mock
apiFn.params = params

return { [apiName]: apiFn }
Expand Down
Loading

0 comments on commit 6733b38

Please sign in to comment.