在项目开发中,前后端分离架构已经成为主流,前端开发进度往往依赖于后端接口的完成度,当后端接口尚未完成时,前端就会由于空数据而写死数据或使用 Express 等 Node 框架来自己造数据从而渲染UI界面,这往往会导致开发成本以及后期联调成本的增高,在此情形下,Mock 的出现,很好的解决了这一问题,它十分方便的模拟中开发中常见的场景,能够轻松的实现增删改查等常规操作,以下是我总结出来的使用方案:
安装Mock
官网地址:mockjs.com/
pmpm i mockjs
pnpm install @types/mockjs -D
语法规则
数据模板定义规范
数据模板中每个属性由 3 个部分构成,分别是属性名(name)、生成规则(rule)、属性值(value),属性名和规则之间用 | 分隔
语法:'name|rule': value
生成的规则有如下 7 种格式:
name|min-max': value'name|count': value'name|min-max.dmin-dmax': value'name|min-max.dcount': value'name|count.dmin-dmax': value'name|count.dcount': value'name|+step': value
具体规则说明请参考官方文档:github.com/nuysoft/Moc…
示例如下:
属性值是字符串
// 语法:'name|min-max': string
// 重复生成一个字符串 *,重复的次数介于 [1,10] 之间
Mock.mock({ 'name|1-10': '*' }) // { name: '*****' }
// 语法:'name|count': string
// 重复生成一个字符串坤,重复的次数等于 2
Mock.mock({ 'name|2': '坤' }) // { name: '坤坤' }
属性值是数字
// 语法:'name|+1': number
// id 属性自增 +1,初始值为 1
Mock.mock({ 'id|+1': 1 }) // 1
// 语法:'name|min-max': number
// 随机生成一个介于 [18,30] 之间的数字
Mock.mock({ 'age|18-30': 18 }) // 26
// 语法:'name|min-max.dmin-dmax': number
// 随机生成一个整数部分介于 [1,100] 之间的数字,小数点后保留 [1,2] 位
Mock.mock({ 'num|1-100.1-2': 10 }) // { num: 73.42 }
// 随机生成一个整数部分固定为 88,小数点后保留 [1,10] 位
Mock.mock({ 'num|88.1-10': 10 }) // { num: 88.1246 }
// 随机生成一个整数部分固定为 66,小数点后保留 2 位
Mock.mock({ 'num|66.2': 10 }) // { num: 66.91 }
属性值是布尔
// 语法:'name|1': boolean
// 随机生成一个布尔值,值为 true 或 false
Mock.mock({ 'bool|1': true }) // { bool: false }
// 语法:'name|min-max': value
// 随机生成一个布尔值,值为 true 的概率为 1/5(min/(min+max)),为 false 的概率为 4/5(max/(min+max))
Mock.mock({ 'bool|1-4': true }) // { bool: false }
属性值是对象
const obj = { name: '坤坤', age: 18, hobby: ['唱', '跳', 'rap'] }
// 语法:'name|count': object
// 从 obj 对象中,随机抽取 2 属性生成一个新对象
Mock.mock({ 'user|2': obj }) // { user: { age: 18, name: '坤坤' } }
// 语法:'name|min-max': object
// 从 obj 对象中,随机抽取 1-3 个属性生成一个新对象
Mock.mock({ 'user|1-3': obj }) // { user: { age: 18, name: '坤坤', hobby: [ '唱', '跳', 'rap' ] } }
属性值是数组
const arr = ['刘一', '陈二', '张三', '李四', '王五', '赵六']
// 语法:'name|1': array
// 随机从 arr 数组中随机抽取 1 个元素作为最终值
Mock.mock({ 'user|1': arr }) // { name: '张三' }
// 语法:'name|+1': array
// 从 arr 数组中,按照顺序,步长为 2 的规则抽取元素作为最终值,默认初始索引为 0,当索引超过数据长度时,会通过取模运算回到起点
// 索引计算公式:(初始索引 + 步长) % 数据长度
Mock.mock({ 'name|+2': arr }) // { name: '刘一' }
Mock.mock({ 'list|3': [{ 'name|+2': arr }] }) // { list: [ { name: '张三' }, { name: '王五' }, { name: '刘一' } ] }
// 语法:'name|min-max': array
// 重复数组值生成一个新数组,重复次数介于 [1,6] 之间
Mock.mock({ 'nameList|1-6': arr }) // { nameList: ['刘一', '陈二','张三', '李四','王五', '赵六','刘一', '陈二','张三', '李四','王五', '赵六'] }
// 语法:'name|count': array
// 重复数组值 2 次生成一个新数组
Mock.mock({ 'nameList|2': arr }) // { nameList: ['刘一', '陈二','张三', '李四','王五', '赵六','刘一', '陈二','张三', '李四','王五', '赵六'] }
属性值是函数
// 语法:'name': function
// 取函数的返回值作为属性值
Mock.mock({ name: () => '坤坤' }) // { name: '坤坤' }
属性值是正则表达式
// 语法:'name': regexp
// 根据正则表达式反向生成可以匹配的字符串
Mock.mock({ name: /[a-z]{3}/ }) // { name: 'bsj' }
数据占位符定义规范
占位符在属性值中占据位置,并不会出现在最终值中,占位符引用的是 Mock.Random() 中的方法
语法:@占位符、@占位符(参数 [, 参数])
列举几个常用的占位符:
数据类型
// 布尔值 Mock.Random.boolean()
Mock.mock({ bool: '@boolean' })
// 自然数(大于等于0的整数)Mock.Random.natural()
Mock.mock({ num: '@natural' })
// 整数 Mock.Random.integer()
Mock.mock({ num: '@integer' })
// 浮点数 Mock.Random.float()
Mock.mock({ num: '@float(1,100,1,3)' })
// 字符串 Mock.Random.character()、Mock.Random.string()
Mock.mock({ str: '@character("upper")' })
Mock.mock({ str: '@string(1,10)' }))
// 整型数组 Mock.Random.range()
Mock.mock({ arr: '@range(0,10,2)' })
日期
// 日期 Mock.Random.date()
Mock.mock({ date: '@date("yyyy-MM-dd")' })
// 时间 Mock.Random.time()
Mock.mock({ time: '@time("HH:mm:ss")' })
// 日期和时间 Mock.Random.datetime()
Mock.mock({ datetime: '@datetime("yyyy-MM-dd HH:mm:ss")' })
// 当前日期时间 Mock.Random.now()
Mock.mock({ now: '@now' })
图片
// 图片大小、背景色、字体色、图片格式、图片名称 Mock.Random.image()
console.log(Mock.mock({ img: '@image("200x100", "#fff", "#000", "png", "img")' }))
颜色
// 格式为 '#RRGGBB' Mock.Random.color()
Mock.mock({ color: '@color' })
// 格式为 '#RRGGBB' Mock.Random.hex()
Mock.mock({ color: '@hex' })
// 格式为 'rgb(r, g, b)' Mock.Random.rgb()
Mock.mock({ color: '@rgb' })
// 格式为 'rgba(r, g, b, a)' Mock.Random.rgba()
Mock.mock({ color: '@rgba' })
// 格式为 'hsl(h, s, l)' Mock.Random.hsl()
Mock.mock({ color: '@hsl' })
文本
// 标题:@ctitle:中文标题 @title:英文标题 Mock.Random.ctitle()、Mock.Random.title()
Mock.mock({ title: '@ctitle(5,10)' })
// 段落:@cparagraph:中文段落 @paragraph:英文段落 Mock.Random.cparagraph()、Mock.Random.paragraph()
Mock.mock({ paragraph: '@cparagraph' })
// 文本:@csentence:中文句子 @sentence:英文句子 Mock.Random.csentence()、Mock.Random.sentence()
Mock.mock({ sentence: '@csentence' })
姓名
// 姓名:@cname:中文名字 @name:英文名字 Mock.Random.cname()、Mock.Random.name()
Mock.mock({ name: '@name' })
// 姓:@cfirst:中文姓 @last:英文姓 Mock.Random.cfirst()、Mock.Random.last()
Mock.mock({ firstName: '@cfirst' })
// 名:@clast:中文名 @first:英文名 Mock.Random.clast()、Mock.Random.first()
Mock.mock({ lastName: '@clast' })
Web
// URL:@url Mock.Random.url()
Mock.mock({ url: '@url("https")' })
// URL:@protocol 协议 Mock.Random.protocol()
Mock.mock({ protocol: '@protocol' })
// 域名:@domain Mock.Random.domain()
Mock.mock({ domain: '@domain' })
// 邮箱:@email Mock.Random.email()
Mock.mock({ email: '@email' })
// ip地址:@ip Mock.Random.ip()
Mock.mock({ ip: '@ip' })
地址
// 区域: @region Mock.Random.region()
Mock.mock({ region: '@region' })
// 省份: @province Mock.Random.province()
Mock.mock({ province: '@province' })
// 城市: @city Mock.Random.city()
Mock.mock({ city: '@city' })
// 县: @county Mock.Random.county()
// 传递参数:true,返回带省市县名的地址
Mock.mock({ county: '@county(true)' })
// 邮编: @zip Mock.Random.zip()
Mock.mock({ zip: '@zip' })
其他
// 首字母大写 @capitalize Mock.Random.capitalize()
Mock.mock({ str: '@capitalize("hello")' })
// 字符串大写 @upper Mock.Random.upper()
Mock.mock({ str: '@upper("hello")' })
// 字符串小写 @lower Mock.Random.lower()
Mock.mock({ str: '@lower("HELLO")' })
// 从数组中随机选取一个元素并返回 @pick Mock.Random.pick()
Mock.mock({ user: '@pick(["刘一", "陈二","张三", "李四","王五", "赵六"])' })
// 打乱数组中元素的顺序并返回 @shuffle Mock.Random.shuffle()
Mock.mock({ user: '@shuffle(["刘一", "陈二","张三", "李四","王五", "赵六"])' })
// 随机生成一个 guid,@guid Mock.Random.guid()
Mock.mock({ guid: '@guid' })
// 随机生成一个 18 位的 id,@id Mock.Random.id()
Mock.mock({ id: '@id' })
模拟请求
基本使用
在项目根目录下,新建 mock 文件夹,新建 user.ts 文件,内容如下:
mock/user.ts
import Mock from 'mockjs'
// 设置请求响应的时间,单位毫秒
Mock.setup({ timeout: '300' })
Mock.mock('/api/getUserInfo', 'get', {
code: 200,
data: {
id: '@id',
name: '@cname',
age: '@integer(18, 60)',
sex: '@pick(男,女)',
email: '@email',
address: '@county(true)'
},
msg: '获取成功!'
})
在 main.ts 文件中引入此文件
src/main.ts
import '../mock/user'
在相应的 vue 文件中向此地址发送请求
<script setup lang="ts">
import axios from 'axios'
async function getUserInfo() {
const { data: res } = await axios.get('/api/getUserInfo')
console.log(res.data)
/*
{
"id": "990000197505125169",
"name": "余军",
"age": 38,
"sex": "男",
"email": "e.mckuflk@mqwyjmgp.sn",
"address": "河南省 洛阳市 老城区"
}
*/
}
getUserInfo()
</script>
vite-plugin-mock
vite-plugin-mock 是一个专门为 vite 设计的插件,能够同时支持开发环境跟生产环境,简化了前端开发中模拟接口和数据生成流程,以下是它的基本使用:
pnpm i vite-plugin-mock -D
在 vite.config.ts 中进行配置
vite.config.ts
import { UserConfigExport, ConfigEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
// https://vitejs.dev/config/
export default ({ command }: ConfigEnv): UserConfigExport => {
return {
plugins: [
vue(),
viteMockServe({
// 忽略以 _ 开头的文件
ignore: /^\_/,
// 模拟数据存放目录
mockPath: 'mock',
// 是否启用 mock 功能
enable: true,
// 是否在控制台显示请求日志
logger: true
})
],
}
}
在项目根目录中新建 mock 文件夹,建立 user.ts 文件,内容如下:
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/api/getToken',
method: 'get',
response: () => {
return {
code: 200,
data: {
token: 'token'
},
msg: '登录成功!'
}
}
},
{
url: '/api/getUserInfo',
method: 'get',
response: () => {
return {
code: 200,
data: {
id: '@id',
name: '@cname',
age: '@integer(18, 60)',
sex: '@pick(男,女)',
email: '@email',
address: '@county(true)',
avatar: '@image(200x200, @color, @cname)'
},
msg: '获取成功!'
}
}
}
] as MockMethod[]
发送请求进行测试,是能够拿到数据的
<template>
<div>
<button @click="getToken">获取token</button>
<button @click="getUserInfo">获取用户信息</button>
</div>
</template>
<script setup lang="ts">
import axios from 'axios'
async function getUserInfo() {
const { data: res } = await axios.get('/api/getUserInfo')
console.log('userInfo', res.data)
}
async function getToken() {
const { data: res } = await axios.get('/api/getToken')
console.log('token', res.data)
}
getToken()
getUserInfo()
</script>
<style scoped></style>
在生产环境中使用 mock,则需要进行如下配置:
在 mock 文件夹下新建 _createProductionServer.ts 文件,文件内容如下:
mock/_createProductionServer.ts
import { createProdMockServer } from 'vite-plugin-mock/client'
const modules: any = import.meta.glob('./**/*.ts', { eager: true })
const mockModules: any[] = []
Object.keys(modules).forEach((key) => {
if (key.includes('/_')) {
return
}
mockModules.push(...modules[key].default)
})
export function setupProdMockServer() {
createProdMockServer(mockModules)
}
在 main.ts 引入使用
main.ts
import { createApp } from 'vue'
import './styles/index.css'
import App from './App.vue'
createApp(App).mount('#app')
if (import.meta.env.PROD) {
import('../mock/_createProductionServer').then(({ setupProdMockServer }) => {
setupProdMockServer()
})
}
接下来我们进行打包测试,运行如下命令即可:
"scripts": {
"build": "vite build",
"preview": "npm run build && vite preview",
},
# 此命令会进行打包,打包结束后会生成一个预览链接:http://localhost:4173/
pnpm run preview
进行测试,小编发现了一个小问题,在生产环境下主动触发接口,会拿不到数据,通过事件调用触发才可以拿到,不知道大家有没有发现这个问题,在翻阅官方 github 的 issue 后,确实有不少人提出在生产环境存在一些问题,需在生产环境下慎用该插件。
vite-plugin-fake-server
此插件是 vite-plugin-mock 插件的平替,使用方式如下:
pnpm i vite-plugin-fake-server -D
在 vite.config.ts 中进行配置
vite.config.ts
import { UserConfigExport, ConfigEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { vitePluginFakeServer } from 'vite-plugin-fake-server'
// https://vitejs.dev/config/
export default ({ command }: ConfigEnv): UserConfigExport => {
return {
plugins: [
vue(),
vitePluginFakeServer({
// mock文件夹下的所有的 *.mock.ts 文件,都会被加载
include: 'mock',
infixName: 'mock',
// 开发环境是否启用
enableDev: true,
// 生产环境是否启用
enableProd: true,
// 是否在控制台显示请求日志
logger: true
})
],
}
}
在项目跟目录下新建 mock 文件,建立 user.mock.ts 文件,内容如下:
mock/user.mock.ts
import { defineFakeRoute } from 'vite-plugin-fake-server/client'
import Mock from 'mockjs'
export default defineFakeRoute([
{
url: '/api/getToken',
method: 'get',
response: () => {
return {
code: 200,
data: {
token: 'token'
},
msg: '登录成功!'
}
}
},
{
url: '/api/getUserInfo',
method: 'get',
response: () => {
return {
code: 200,
data: Mock.mock({
id: '@id',
name: '@cname',
age: '@integer(18, 60)',
sex: '@pick(男,女)',
email: '@email',
address: '@county(true)',
avatar: '@image(200x200, @color, @cname)'
})
}
}
}
])
发送请求进行测试,获取数据成功,测试代码同 vite-plugin-mock,这里不在描述
接着进行打包预览测试,命令同上
pnpm run preview
进行测试发现无论是主动触发还是事件调用都能拿到数据,至此,在前端项目中使用 Mock 这篇文章就到此结束啦~~~