什么是Mock数据
Mock 可以翻译为“模拟”,在前端开发中通常指模拟后端接口和数据。
在软件开发过程中,Mock数据指的是一种虚拟的测试数据,用于模拟真实的数据情况。它可以用来替代真实的数据源,从而实现在没有真实数据的情况下进行开发和测试的需求。
我们在开发的时候,一般都是前后端同时开发,尤其是某些后台系统开发时,很多前端同学分分钟就可以做完静态页面的开发,然后就是摸鱼时间,等待后端的接口。如果你的后端同事非常配合,愿意在设计数据库的时候顺便定好返回的数据结构和字段,并且联调时间很紧张的时候,这时候你应该考虑着手去做一下前端 mock,为联调争取时间。
Mock数据的使用不仅可以提高开发效率,还可以避免与真实数据源的依赖性,减少开发过程中的等待时间,以及降低测试环境的复杂性。
使用Mock数据的优点
简单性:不需要复杂的配置和部署过程,可以快速地创建和管理模拟数据。
灵活性:可以满足不同的开发需求,例如支持不同的请求类型、不同的请求方式等。
真实性:能够生成更接近真实的数据,可以模拟不同的数据类型、格式和结构。
维护性:可以方便地更新和修改模拟数据。
合作性: 可以在团队内方便的共享模拟数据。
Mock数据使用步骤
一、安装依赖mockjs、vite-plugin-mock
提供本地和生产模拟服务。
vite 的数据模拟插件,是基于 vite.js 开发的。 并同时支持本地环境和生产环境。 Connect 服务中间件在本地使用,mockjs 在生产环境中使用。
npm i mockjs vite-plugin-mock --save-dev
二、vite.config.ts 文件中配置
// vite.config.ts import { defineConfig } from 'vite' import { viteMockServe } from 'vite-plugin-mock' import vue from '@vitejs/plugin-vue' export default defineConfig(({ command }) => { return { plugins: [ vue(), viteMockServe({ mockPath: 'mock', // mock文件夹路径 enable: command === 'serve' // 只有开发环境才开启mock }), ], } })
- viteMockServe 配置
{ mockPath?: string; ignore?: RegExp | ((fileName: string) => boolean); watchFiles?: boolean; enable?: boolean; ignoreFiles?: string[]; configPath?: string; logger?:boolean; }
参数 | 类型 | 默认值 | 说明 |
mockPath | string | mock | 设置模拟.ts 文件的存储文件夹 |
ignore | RegExp | undefined | 自动读取模拟.ts 文件时,请忽略指定格式的文件 |
watchFiles | boolean | true | 设置是否监视mockPath对应的文件夹内文件中的更改 |
enable | boolean | true | 是否启用 mock 功能 |
configPath | string | vite.mock.config.ts | 设置模拟读取的数据条目 |
logger | boolean | true | 是否在控制台显示请求日志 |
三、在根目录下创建mock文件
项目文件夹下新建mock文件夹,用于存放本地mock文件
// mock/user.ts const createUserList = () => { return [ { userId: 1, avatar: 'https://pic1.zhimg.com/80/v2-083faf550543c1e9f134b56b3322ee3c_720w.webp', username: 'admin', password: '123456789', desc: '下船不谈船里事', roles: ['平台管理员'], buttons: ['cuser.detail'], routes: ['home'], token: 'Admin Token' }, { userId: 2, avatar: 'https://pic1.zhimg.com/80/v2-e1427f6a21122ac163ff98d24f55d372_720w.webp', username: 'system', password: '123456789', desc: '旧人不谈近况,新人不讲过往', roles: ['系统管理员'], buttons: ['cuser.detail', 'cuser.user'], routes: ['home'], token: 'Admin Token' } ] } export default [ // 用户登录接口 { url: '/api/user/login', method: 'post', response: ({ body }: any) => { // 获取请求体携带过来的用户名与密码 const { username, password } = body // 调用获取用户信息函数,用于判断是否有此用户 const checkUser = createUserList().find( (item) => item.username === username && item.password === password ) // 没有用户则返回失败信息 if (!checkUser) { return { code: 201, data: { message: '账号或者密码不正确' } } } // 如果有返回成功信息 const { token } = checkUser return { code: 200, data: { token } } } }, // 获取用户信息接口 { url: '/api/user/info', method: 'get', response: (request: any) => { // 获取请求头携带的 token const token = request.headers.token // 查看用户信息数据中是否包含有此 token 的用户 const checkUser = createUserList().find((item) => item.token === token) // 没有就返回失败信息 if (!checkUser) { return { code: 201, data: { message: '获取用户信息失败' } } } // 有就返回成功信息 return { code: 200, data: { checkUser } } } } ]
四、编写api接口调用文件
1、src文件夹下新建utils/request.ts
// utils/request.ts import axios from "axios"; //创建一个axios实例 const request = axios.create({ baseURL: '', timeout: 20000, }); // 添加请求拦截器 request.interceptors.request.use( function (config) { // 请求地址携带时间戳 const _t = new Date().getTime() config.url += `?${_t}` // 请求头携带token config.headers['token'] = localStorage.getItem('token') || '' // 在发送请求之前做些什么 // console.log('我要准备请求啦------') // console.log(config, '请求配置') return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); } ); // 添加响应拦截器 request.interceptors.response.use( function (response) { // 对响应数据做点什么 // console.log('我接收到响应数据啦------') // console.log(response, '响应配置') if (response.status === 200) { return Promise.resolve(response.data) } else { return Promise.reject(response) } }, function (error) { // 对响应错误做点什么 if (error && error.response) { switch (error.response.status) { case 400: error.message = '错误请求'; break; case 401: error.message = '未授权,请重新登录'; break; case 403: error.message = '拒绝访问'; break; case 404: error.message = '请求错误,未找到该资源'; break; case 405: error.message = '请求方法未允许'; break; case 408: error.message = '请求超时'; break; case 500: error.message = '服务器端出错'; break; case 501: error.message = '网络未实现'; break; case 502: error.message = '网络错误'; break; case 503: error.message = '服务不可用'; break; case 504: error.message = '网络超时'; break; case 505: error.message = 'http版本不支持该请求'; break; default: error.message = `未知错误${error.response.status}`; } } else { error.message = "连接到服务器失败"; } return Promise.reject(error); } ); /* * get请求:从服务器端获取数据 * url:请求地址 * params:参数 * */ export function get(url:string, params = {}) { return new Promise((resolve, reject) => { request({ url: url, method: 'get', params: params }).then(response => { resolve(response); }).catch(error => { reject(error); }); }); } /* * post请求:向服务器端提交数据 * url:请求地址 * params:参数 * */ export function post(url:string, params = {}) { return new Promise((resolve, reject) => { request({ url: url, method: 'post', data: params }).then(response => { resolve(response); }).catch(error => { reject(error); }); }); } // 对外暴露请求方法 export default { get, post }
2、src文件夹下新建api/user.ts
// api/user.ts import { get, post } from './request'; const api = { login: '/api/user/login', users: '/api/user/info' } //登录 export const login = (params: any) => { return post(api.login, params).then((res: any) => { if (res.code === 200) { localStorage.setItem('token', res.data.token); } return Promise.resolve(res); }) } //获取用户信息 export const getUserInfo = () => { const token = localStorage.getItem('token'); if (!token) return Promise.reject(new Error('用户未登录')); return get(api.users); }
五、业务页面调用
// App.vue import { onBeforeMount } from 'vue' import {login,getUserInfo} from "./utils/user" onBeforeMount(async () => { // 登录 const user = await login({ username: 'admin', password: '123456789' }) console.log(user) // 获取用户信息 const users = await getUserInfo() console.log(users) }) //// App.vue import { onBeforeMount } from 'vue' import {login,getUserInfo} from "./utils/user" onBeforeMount(async () => { // 登录 const user = await login({ username: 'admin', password: '123456789' }) console.log(user) // 获取用户信息 const users = await getUserInfo() console.log(users) })
六、Mock的用法规则
import mockJS from 'mockjs' const userList = mockJS.mock({ // 属性 list 的值是一个数组,其中含有 1 到 10 个元素 'list|1-10': [ { // 随机生成id号 id: '@id', // 随机生成中文姓名 name: '@cname', // 属性 id 是一个自增数,起始值为 1,每次增 1 'id|+1': 1, // 随机生成ip地址 ip: '@ip', // 随机生成省市区地址 address:'@county(true)', // 随机生成邮政编码 zip:'@zip', // 随机生成18-70之间的年龄 "age|18-70": 20, // 随机生成日期 date: '@date("yyyy-MM-dd")', // 随机生成头像 avatar:"@image('200x200')", } ] })
七、在生产环境中的使用
1、创建mockProdServer.ts 文件
// mockProdServer.ts import { createProdMockServer } from 'vite-plugin-mock/client' // 逐一导入您的mock.ts文件 // 如果使用vite.mock.config.ts,只需直接导入文件 // 可以使用 import.meta.glob功能来进行全部导入 import testModule from '../mock/test' export function setupProdMockServer() { createProdMockServer([...testModule]) }
2、配置 vite-plugin-mock
import { viteMockServe } from 'vite-plugin-mock' import { UserConfigExport, ConfigEnv } from 'vite' export default ({ command }: ConfigEnv): UserConfigExport => { return { plugins: [ viteMockServe({ mockPath: 'mock', // 根据项目配置。可以配置在.env文件 enable: true, }), ], } }
3、注意事项
- 无法在 mock.ts 文件中使用 node 模块,否则生产环境将失败
- 模拟数据如果用于生产环境,仅适用于某些测试环境。 不要在正式环境中打开它,以避免不必要的错误。 同时,在生产环境中,它可能会影响正常的 Ajax 请求,例如文件上传/下载失败等。