记一次前端Vue3+Vite+TS项目中使用Mock.js + vite-plugin-mock插件发布到生产(线上)环境填坑汇总
一、关于 Mock 数据
Mock:在前端开发中通常指模拟后端接口和数据。
开发过程中,一般都是前后端同时开发,尤其是某些后台系统开发时,很多前端同学分分钟就可以做完静态页面的开发,然后就是摸鱼时间,等待后端的接口。如果你的后端同事非常配合,愿意在设计数据库的时候顺便定好返回的数据结构和字段,并且联调时间很紧张的时候,这时候你应该考虑着手去做一下前端 mock,为联调争取时间。
Mock 优点
- 简单: 不需要复杂的配置和部署过程,可以快速地创建和管理模拟数据;
- 灵活: 可以满足不同的开发需求,例如支持不同的请求类型、不同的请求方式等;
- 真实: 能够生成更接近真实的数据,可以模拟不同的数据类型、格式和结构;
- 可维护: 可以方便地更新和修改模拟数据;
- 共享: 可以在团队内方便的共享模拟数据。
二、Mock 数据使用
1. 安装依赖 mockjs、vite-plugin-mock
vite 的数据模拟插件,是基于 vite.js 开发的。 支持本地环境和生产环境服务。
npm i mockjs vite-plugin-mock --save-dev
2. 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: [viteMockServe({mockPath: "./mock/", // 目录位置logger: true, // 是否在控制台显示请求日志supportTs: true, // 是否读取ts文件模块localEnabled: env.VITE_APP_OPEN_MOCK === "true", // 设置是否启用本地mock文件prodEnabled: env.VITE_APP_OPEN_MOCK === "true", // 设置打包是否启用mock功能// 这样可以控制关闭mock的时候不让mock打包到最终代码内injectCode: `import { setupProdMockServer } from './mockProdServer';setupProdMockServer();`}),],// 省略其它配置...}
})
3. 开发环境使用
-
在根目录下创建 mock 文件夹,用于存放本地 mock 文件,内部创建 user.ts
// mock/user.tsconst 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/info',method: 'get',response: (request: any) => {// 获取请求头携带的 tokenconst 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}}}} ]
-
src文件夹下新建 utils/request.ts
// utils/request.ts import axios from "axios";//创建一个axios实例 const request = axios.create({baseURL: '/api',timeout: 20000, });// 添加请求拦截器 request.interceptors.request.use(function (config) {// 请求地址携带时间戳const _t = new Date().getTime()config.url += `?${_t}`// 请求头携带tokenconfig.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 }
-
src文件夹下新建 api/user.ts
// api/user.tsimport { get, post } from './request';const api = {users: '/api/user/info' }//获取用户信息 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 { getUserInfo } from "./api/user"onBeforeMount(async () => {// 获取用户信息const users = await getUserInfo()console.log(users) })
三、在生产(线上)环境中的使用(填坑)
1. 配置 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: [viteMockServe({mockPath: "./mock/", // 目录位置logger: true, // 是否在控制台显示请求日志supportTs: true, // 是否读取ts文件模块localEnabled: env.VITE_APP_OPEN_MOCK === "true", // 设置是否启用本地mock文件prodEnabled: env.VITE_APP_OPEN_MOCK === "true", // 设置打包是否启用mock功能// 这样可以控制关闭mock的时候不让mock打包到最终代码内injectCode: `import { setupProdMockServer } from './mockProdServer';setupProdMockServer();`}),],// 省略其它配置...}
})
填坑一、prodEnabled需要配置成true才能在生产环境打包mock
2. 创建 mockProdServer.ts 文件
在根目录下新建文件 mockProdServer.ts
// mockProdServer.ts
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';// 导入所有的 mock 模块
const modules = import.meta.globEager('./mock/*.ts');
const mockModules: any[] = [];Object.keys(modules).forEach((key) => {if (key.includes('/_')) {return;}mockModules.push(...modules[key].default);
});export function setupProdMockServer() {createProdMockServer(mockModules);
}
填坑二、 这里 import.meta.globEager() 引入的文件路径可以使用通配符,需要注意书写格式,要能够成功引入这些ts才行,引入不成功,也不会报错,打包后在生产环境非常难以debug
3. main.ts 中引用上面这个模块的 setupProdMockServer() 并调用
// src/main.ts
import { setupProdMockServer } from '../mockProdServer'
setupProdMockServer()
填坑三、这里一定要调用否则发布到生产的时候就无法访问接口,接口会返回405,404的错误
4. 打包
"scripts":{"build:pro": "vite build --mode pro"
}