8.7 KiB
8.7 KiB
Mock 与联调
Mock
本项目中的接口采用的是Mock.js
配合axios
进行接口对接模拟,默认是所有环境下都开启mock
。如需不需要,可以删除src/main.ts
中引入mock
的代码。
具体配置代码可查看src/mock/index.ts
::: warning 注意
自定义mock
接口的时候,可能会出现跨域的问题,这时候开发者可以自行在vite.config.ts
中自行代理。
Mock的模拟请求,在浏览器中是看不到请求记录的,这点也是一个弊端,开发者只能通过代码打印的形式去查看返回的结果,后续考虑还有没有更优的方案。 :::
mock
接口的写法可参考src/mock/example/index.ts
import Mock from 'mockjs'
import { toAnyString } from '@/utils'
let List: any[] = []
const count = 100
const baseContent =
'<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
for (let i = 0; i < count; i++) {
List.push(
Mock.mock({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
// image_uri
})
)
}
export default [
// 列表接口
{
url: 'http://mockjs.test.cn/example/list',
type: 'get',
response: (config: any) => {
const { title, pageIndex, pageSize } = config.query
const mockList = List.filter((item) => {
if (title && item.title.indexOf(title) < 0) return false
return true
})
const pageList = mockList.filter(
(item, index) =>
index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
)
return {
code: '0000',
data: {
total: mockList.length,
list: pageList
}
}
}
},
// 删除接口
{
url: 'http://mockjs.test.cn/example/delete',
type: 'post',
response: (config: any) => {
const ids = config.body.ids
if (!ids) {
return {
code: '500',
message: '请选择需要删除的数据'
}
} else {
let i = List.length
while (i--) {
if (ids.indexOf(List[i].id) !== -1) {
List.splice(i, 1)
}
}
return {
code: '0000',
data: 'success'
}
}
}
},
// 详情接口
{
url: 'http://mockjs.test.cn/example/detail',
type: 'get',
response: (config: any) => {
const { id } = config.query
for (const example of List) {
if (example.id === id) {
return {
code: '0000',
data: example
}
}
}
}
},
// 保存接口
{
url: 'http://mockjs.test.cn/example/save',
type: 'post',
response: (config: any) => {
const data = config.body
if (!data.id) {
List = [
Object.assign(data, {
id: toAnyString(),
importance: Number(data.importance)
})
].concat(List)
return {
code: '0000',
data: 'success'
}
} else {
List.map((item) => {
if (item.id === data.id) {
for (const key in item) {
if (key === 'importance') {
item[key] = Number(data[key])
} else {
item[key] = data[key]
}
}
}
})
return {
code: '0000',
data: 'success'
}
}
}
}
]
Axios
本项目中的所有接口请求都是基于axios.js来进行的。
为了便于后期的维护,本项目中对axios
进行了二次封装,从而可以对接口请求进行统一拦截。
配置项
同时,可供了一些axios
的全局配置,具体代码src/axios-config/config.ts
,开发者可自行更改扩展。
/**
* request全局配置
*/
const config: {
base_url: {
base: string
dev: string
pro: string
test: string
}
result_code: number | string
default_headers:
| 'application/json'
| 'application/x-www-form-urlencoded'
| 'multipart/form-data'
request_timeout: number
} = {
/**
* api请求基础路径
*/
base_url: {
// 开发环境接口前缀
base: 'http://mockjs.test.cn',
// 打包开发环境接口前缀
dev: 'http://mockjs.test.cn',
// 打包生产环境接口前缀
pro: 'http://mockjs.test.cn',
// 打包测试环境接口前缀
test: 'http://mockjs.test.cn'
},
/**
* 接口成功返回状态码
*/
result_code: '0000',
/**
* 接口请求超时时间
*/
request_timeout: 60000,
/**
* 默认接口请求类型
* 可选值:application/x-www-form-urlencoded multipart/form-data
*/
default_headers: 'application/json'
}
export default config
请求方法
为了便于维护,本项目中对所有请求方式的接口进行封装,开发者只需要参数对应参数即可使用如get
、post
请求。
具体代码src/axios-config/index.ts
,开发者可自行更改扩展。
import request from './request'
import { appStore } from '@/store/modules/app'
import config from './config'
import { AxiosPromise, ResponseType } from 'axios'
const { default_headers } = config
export interface Config {
params?: any
data?: any
url?: string
method?: 'get' | 'post' | 'delete' | 'put'
headersType?: string
responseType?: ResponseType
}
function fetch({
url,
method,
params,
data,
headersType,
responseType
}: Config): AxiosPromise {
return request({
url: url,
method,
params: appStore.requestTime
? { time: new Date().getTime(), ...(params || {}) }
: params,
data,
responseType: responseType,
headers: {
'Content-Type': headersType || default_headers
}
})
}
export default fetch
请求拦截
具体代码src/axios-config/request.ts
,开发者可自行更改扩展。
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { Message } from '_c/Message'
import qs from 'qs'
import config from './config'
const { result_code, base_url } = config
export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH as string]
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: PATH_URL, // api 的 base_url
timeout: config.request_timeout // 请求超时时间
})
// request拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
if (
config.method === 'post' &&
(config.headers as any)['Content-Type'] === 'application/x-www-form-urlencoded'
) {
config.data = qs.stringify(config.data)
}
// get参数编码
if (config.method === 'get' && config.params) {
let url = config.url as string
url += '?'
const keys = Object.keys(config.params)
for (const key of keys) {
if (config.params[key] !== void 0 && config.params[key] !== null) {
url += `${key}=${encodeURIComponent(config.params[key])}&`
}
}
url = url.substring(0, url.length - 1)
config.params = {}
config.url = url
}
return config
},
(error: AxiosError) => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
(response: AxiosResponse<any>) => {
if (response.config.responseType === 'blob') {
// 如果是文件流,直接过
return response
} else if (response.data.code === result_code) {
return response.data
} else {
Message.error(response.data.message)
}
},
(error: AxiosError) => {
console.log('err' + error) // for debug
Message.error(error.message)
return Promise.reject(error)
}
)
export default service
Api管理
在之前,我一直在纠结,api
接口到底是集中管理,还是分模块各自管理,在之后的几个公司项目的实践中,在项目越来越大,一个模块有可能调用了几十个接口,那这时候在集中管理api
,会导致api
的来源不明确,不利于查找对应api
接口。所以现在比较偏向分模块进行api
管理,这样更便于后期的维护,各模块只关心和维护模块中使用的api
接口。
import fetch from '@/axios-config'
export const loginApi = ({ data }: any) => {
return fetch({ url: '/user/login', method: 'post', data })
}
export const getRoleDetApi = ({ params }: any) => {
return fetch({ url: '/role/detail', method: 'get', params })
}