参考文章mockJs 妈妈再也不用担心我没有后端接口啦

初识 Mock.js

Mock.js 官网http://mockjs.com/

注意:Mock.js 早在几年之前就不再更新维护,建议非必要不使用它。

Mock.js 是模拟数据的第三方库,它是一个用于生成随机数据和拦截 Ajax 请求的 JavaScript 库,非常适合在前端开发中模拟后端 API 和生成大量测试数据。

使用场景:在项目中拦截请求,构造假数据。

Mock.js 的主要功能包括:

  • 生成随机数据:通过定义数据模板,Mock.js 可以生成各种类型的随机数据,例如文字、数字、日期和自定义格式。

  • 拦截 Ajax 请求:Mock.js 可以捕获和模拟 Ajax 请求,返回模拟的响应数据,这对于前端开发和测试非常有用,尤其是在后端服务尚未开发完成时。

Mock.js 简单使用步骤

安装 Mock.js:在你的项目中,首先需要安装 Mock.js。打开终端,进入你的项目目录,然后运行以下命令:

npm install mockjs

# 如果是TS项目,则还需执行下面的命令
npm install -D @types/mockjs

创建 Mock 数据规则:在项目中创建一个专门的目录(例如 src/mocks),用于存放所有的 Mock 数据规则和配置。创建一个简单的 Mock 规则文件 。src/mocks/index.js

import { mock } from 'mockjs'

const data = mock({
    "status": 200,
    'data|1-10': [{
        'id|+1': 1,
        'name': '@name', // 随机生成名字
        'age|18-60': 1  // 随机生成18至60之间的数字
    }]
})

console.log('Mock.js is loaded and mock API is set.')
console.log(data)

Mock 之生成随机数据

更多使用示例介绍 __ 数据模板定义、数据占位符参考示例:http://mockjs.com/examples.html

1. 数据模板定义规范

数据模板中每个属性由 3 部分构成属性名、生成规则、属性值,数据模版格式如下

"属性名|生成规则": 属性值
  • 属性名:用来指定生成数据的规则,可以加上规则字符串来控制生成的数据。

  • 属性值:用来指示生成数据的类型和内容,可以是具体的值、类型占位符、或者函数。

1.1. 生成规则

生成规则有以下 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
  • dmin 最少小数位,dmax 最多小数位

  • step 递增

  • dcount 固定位数的小数位

1.2. String

  • 'name|min-max': string 重复string,次数为min-max

  • 'name|count': string 重复string,次数为 count

 // string 值是字符串
"name1|1-10": 'a',      // 重复 1-10 次,每次重复的字符串为 'a'
"name2|3": '**',        // 重复 3 次,每次重复的字符串为 '**'

1.3. Number

  • 'name|+1': number 值加1,初始值为number

  • 'name|min-max': number 生成一个 min-max 之间的整数

  • 'name|min-max.dmin-dmax': number 生成一个浮点数,整数部分在 min-max 之间,小数部分保留 dmin-dmax 位

  • 'name|count.dcount: number 生成一个整数部分为 count,小数部分保留 dcount 位的小数

"numberList|3": [
    {
        // number 值是数字
        "number1|+1": 1,            // 递增,从 1 开始递增 1
        "number2|1-5": 1,           // 范围随机,随机生成 1-5 之间的数字
        "number3|1-100.1-10": 1,    // 随机小数,整数部分1-100,小数部分1-10位 =》 随机生成 1-100 之间的小数,小数点后 1-10 位
        "number4|1.1-10": 1,        // 随机小数,整数部分1,小数部分1-10位 =》 随机生成 1-10 之间的小数
        "number5|1.1": 1,           // 随机小数,整数部分1,小数部分保留1位 =》 随机生成 1-10 之间的小数
    }
]

1.4. Boolean

  • name|1': boolean 随机生成一个布尔值,值为 true 的概率为 1/2

  • name|min-max': boolean 随机生成一个布尔值,值为 boolean 的概率是 min / (min + max),值为 !boolean 的概率是 max / (min + max)

// boolean 值是布尔
"boolean1|1": true,     // 随机生成 true 或 false, 值为 true 的概率为 1/2
"boolean2|1-2": false,  // 随机生成一个布尔值,值为 false 的概率是 1/3 min / (min + max),值为 true 的概率是 2/3 max / (min + max)

1.5. Object

  • 'name|count': object 从属性值 object 中随机选取 count 个属性

  • 'name|min-max': object 从属性值 object 中随机选取 min-max 个属性

// object 值是对象
 "object1|2": {
     name: "张三",
     age: 18,
     sex: "男"
 },
 "object2|1-2": {
     name: "张三",
     age: 18,
     sex: "男",
     address: "北京市东城区"
 }

1.6. Array

  • 'name|1': array 从属性值 array 中随机选取 1 个元素,作为最终值

  • 'name|+1': array 从属性值 array 中 顺序 选取 1 个元素,作为最终值

  • 'name|min-max': array 通过重复属性值 array 生成一个新数组,重复次数为 [min,max]

  • 'name|count': array 通过重复属性值 array 生成一个新数组,重复次数为 count

// array 值是数组
"array1|1": [1,2,3,4,5],
"array2|+1": [1,2,3,4,5],
"array3|1-3": [1,2,3,4,5],
"array4|3": [1,2,3,4,5]

1.7. Function

  • 'name': function 执行这个函数

// function 值是函数
'function': function() {
    return '张三'
}

1.8. RegExp

  • 'name': regexp 根据正则表达式 regexp 反向生成可以匹配它的字符串。用于生成自定义格式的字符串

// regexp 值是正则
"regexp|2":[
    {
        "email": /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/,
        "phone": /^1[3456789]\d{9}$/,
        "idCard": /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
        "ip": /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/,
    }
]

1.9. Path

  • Absolute Path

  • Relative Path

2. 数据占位符

数据占位符参考示例:http://mockjs.com/examples.html#DPD

Mock.js 内置了丰富的数据占位符用于生成各种类型和格式的数据。这些占位符通常以 @ 符号开头。

  • 占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。

  • @来标识占位符

  • 占位符引用的是 Mock.Random 中的方法

  • Mock.Random.extend () 来扩展自定义占位符

  • 占位符 会优先引用 数据模板 中的属性

  • 占位符 支持 相对路径 和 绝对路径

占位符 格式:

@占位符
@占位符(参数)

数据占位符使用

  1. Random.xxx()// 使用 Random 函数, xxx 为 random 对应方法

  2. Mock.mock ('@xxx') // 使用占位符,对应 @xxx 的占位符

2.1. Random 方法总结

// Basic: boolean natural integer float character string range
Random.boolean() 														// 随机布尔值
Random.natural( min?, max? )											// 随机自然数 min-max之间
Random.integer( min?, max? )  											// 随机整数 min-max之间
Random.float( min?, max?, dmin?, dmax? )   								// 随机浮点数
Random.character( pool? )  		// 随机字母  pool对应有(lower[小写]/upper[大写]/number[数字]/symbol[符号])
Random.string( pool?, min?, max? )     									// 随机字符串
Random.range(start?, stop, step?)     									// 随机范围数字组成的数组

// Date: date time datetime now
Random.date( format? ) 							 						 // 随机日期
Random.time( format? )  												 // 随机时间
Random.datetime( format? )  											 // 随机日期+时间
Random.now( unit?, format? )  											// 当前时间

// Image: image dataImage
Random.image( size?, background?, foreground?, format?, text? )  	    // 图片占位
Random.dataImage( size?, text? )  										// 生成base64格式图片

// Color: color hex rgb rgba hsl
Random.color()  														// 随机颜色
Random.hex()
Random.rgb()  															// 随机rgb颜色
Random.rgba()   														// rgba颜色值
Random.hsl()

// Text: paragraph sentence word title cparagraph csentence cword ctitle
Random.paragraph( min?, max? )   										// 段落
Random.sentence( min?, max? )
Random.word( min?, max? )
Random.title( min?, max? )
Random.cparagraph( min?, max? )   										// 中文段落
Random.csentence( min?, max? )   										// 中文句子
Random.cword( pool?, min?, max? )  										// 汉字
Random.ctitle( min?, max? ) 											// 标题

// Name: first last name cfirst clast cname
Random.first() 															// 姓
Random.last()  															// 名
Random.name( middle? )  												// 姓名
Random.cfirst()  														// 中文姓
Random.clast()  														// 中文名
Random.cname()  														// 中文姓名

// Web: url domain protocol tld email ip
Random.url()  															// 随机URL
Random.domain()  														// 随机域名
Random.protocol()														// 随机网络协议
Random.tld()															// 随机顶级域名
Random.email()  														// 邮箱
Random.ip()

// Address: region province city county zip
Random.region()
Random.province()  														// 省
Random.city( prefix? )  												// 市   (prefix是否带前缀省,值为boolean)
Random.county( prefix? )  												// 区县  (prefix是否带缀省,市)
Random.zip()															// 邮政编码
              
// Helper: capitalize upper lower pick shuffle
Random.capitalize( word )												// 转换首字母大写									
Random.upper( str )														// 转换为大写字母
Random.lower( str )														// 转换为小写字母
Random.pick( arr )														// 数组中随机选择一个元素返回
Random.shuffle( arr )													// 数组元素随机打乱顺序后返回新数组
              
// Miscellaneous: guid id increment
Random.guid()															// 返回一个新的全局唯一标识符(GUID/UUID)
Random.id()             												// 返回一个仿真的身份证号码(模拟中国的身份证格式)
Random.increment( step? )  												// 递增,生成一个递增的数字,每次调用时根据指定的步长 step 增加

以下是一些常用的占位符类别及示例:

2.2. Basic

Random.boolean()												// 随机布尔值
Random.natural( min?, max? )  									// 随机自然数 min-max之间
Random.integer( min?, max? )  									// 随机整数 min-max之间
Random.float( min?, max?, dmin?, dmax? )   						// 随机浮点数
Random.character( pool? )  					 					// 随机字母  pool对应有(lower[小写]/upper[大写]/number[数字]/symbol[符号])
Random.string( pool?, min?, max? )   							// 随机字符串
Random.range(start?, stop, step?)   							// 随机范围数字组成的数组
"布尔值": Random.boolean(),
"自然数": Random.natural(1, 10),                                      // 1 到 10 之间的自然数
"整数": Random.integer(1, 100),                                       // 1 到 100 之间的整数
"浮点数": Random.float( 0, 10, 2, 2 ),			      				  // 0 到 10 之间,小数部分保留 2 位的浮点数
"小写字母": Random.character( "lower" ),  					 		  // 随机字母 (lower[小写]/upper[大写]/number[数字]/symbol[符号])
"符号": Random.character( "symbol" ),  					 		      // 随机生成符号
"字符串": Random.string( "lower", 5, 10 ),   				  		  // 随机生成字符串
"数组": Random.range(1, 10,2)                            			  // 1开始到10结束,步长为2      

2.3. Date

时间日期类

  • @date("yyyy-MM-dd"):生成符合格式的日期。

  • @time("HH:mm:ss"):生成符合格式的时间。

  • @datetime("yyyy-MM-dd HH:mm:ss"):生成符合格式的日期和时间。

Random.date( format? ) 												// 随机日期
Random.time( format? )  											// 随机时间
Random.datetime( format? )  										// 随机日期+时间
Random.now( unit?, format? )  										// 当前时间
"日期": Random.date(),
"时间": Random.time(),
"日期+时间": Random.datetime('"yyyy-MM-dd HH:mm:ss"'),
"当前时间": Random.now()

2.4. *Image

图片网站:Dynamic Dummy Image Generator

@image( size?, background?, foreground?, format?, text? )

size 尺寸 background 背景色 foreground 前景色 format 后缀 text 文本

图像类

  • @image('200x100', '#FFCC33', '#FFF', 'png', 'Hello'):生成一个尺寸为200x100,背景色为#FFCC33,前景色为#FFF,格式为png的图像,图像中的文本为Hello。

Random.image( size?, background?, foreground?, format?, text? ) 			// 图片占位
Random.dataImage( size?, text? )  											// 生成base64格式图片
import {mock, Random} from 'mockjs'

const data = mock({
    "imgList|3": [
        {
            "img1": "@image( '200x100', '#FFCC33', '#FFF', 'png', 'Hello' )",
            "img2": Random.image( '200x100', '#FFCC33', '#FFF', 'png', 'Hello' )
        }
    ]
})

console.log('Mock.js is loaded and mock API is set.')
console.log(data)

2.5. Color

颜色类

Random.color()  				// 随机颜色十六进制
Random.hex()
Random.rgb()  					// 随机rgb颜色
Random.rgba()   				// rgba颜色值
Random.hsl()
"颜色1": Random.color(),
"颜色2": Random.hex(),
"颜色3": Random.rgb(),
"颜色4": Random.rgba(),
"颜色5": Random.hsl()

2.6. Text

文本类

  • @sentence(3, 5):生成一个包含 3 到 5 个单词的句子。

  • @title(1, 3):生成一个 1 到 3 个单词的标题。

  • @word(3, 5):生成一个长度在 3 到 5 之间的单词。

Random.paragraph( min?, max? )   														// 段落
Random.sentence( min?, max? )
Random.word( min?, max? )
Random.title( min?, max? )
Random.cparagraph( min?, max? )   														// 中文段落
Random.csentence( min?, max? )   														// 中文句子
Random.cword( pool?, min?, max? )  														// 汉字
Random.ctitle( min?, max? )																// 标题

2.7. Name

名字类

  • @name:生成一个常见的名字。

Random.first() 							// 姓
Random.last()  							// 名
Random.name( middle? )  				// 姓名
Random.cfirst()  						// 中文姓
Random.clast()  						// 中文名
Random.cname()  						// 中文姓名
"姓": Random.first(),
"名": Random.last(),
"姓名": Random.name(),
"中文姓": Random.cfirst(),
"中文名": Random.clast(),
"中文姓名": Random.cname()

2.8. Web

网址类

  • @url:生成一个 URL。

Random.url()  									// 随机URL
Random.domain()  								// 随机域名
Random.protocol()								// 随机网络协议
Random.tld()									// 随机顶级域名
Random.email()  								// 邮箱
Random.ip()
"url": Random.url('https'),  																									
"域名": Random.domain(),  																									
"协议": Random.protocol(),																									
"顶级域名": Random.tld(),																											
"邮箱": Random.email('gmail.com'),  																									
"ip": Random.ip()

2.9. Address

地址类

Random.region()
Random.province()  										// 省
Random.city( prefix? )  								// 市   (prefix是否带前缀省,值为boolean)
Random.county( prefix? )  								// 区县  (prefix是否带缀省,市)
Random.zip()											// 邮政编码
"地域": Random.region(),
"省份": Random.province(),
"市区": Random.city(),
"区县": Random.county(),
"邮编": Random.zip()

2.10. 扩展占位符

import {mock, Random} from 'mockjs'

Random.extend({
    names: function () {
        return this.name() + '-' + this.cname()
    },
    bili: function () {
        return this.pick(['点赞','投币','关注'])
    }
})

const data = mock({
    "status": 200,
    'data|1-10': [{
        'name': '@names',
        'bili': '@bili'
    }]
})

console.log('Mock.js is loaded and mock API is set.')
console.log(data)

Mock 之拦截请求

1. 拦截 axios 请求示例

1.1. 简单示例

前置条件:注意务必确保项目中已安装了 axios、mockjs 依赖。

封装 axios:这是封装好的 axios 请求(路径为 /src/api/index.ts ):

import axios from 'axios'

axios.defaults.baseURL = 'http://localhost:3000'

export default axios

mock 拦截/src/mocks/mock.ts,注意确保请求地址前缀与 axios 设置的 baseURL 保持一致。

import { mock } from 'mockjs'

mock('http://localhost:3000/users', {
    "status": 200,
    "message": "success",
    "data|3-10": [
        {
            "id|+1": 1,
            "name": "@name",
            "age|18-30": 1,
            "address": "@county(true)"
        }
    ]
})

调用接口测试:在 App.vue 首页中调用接口,此处通过控制台打印可看到模拟的数据。

<script setup lang="ts">
import { RouterView } from 'vue-router'
import {onMounted} from "vue"
import axios from '@/api/index'
import '@/mocks/mock'

const getUsers =  async () => {
  const res = await axios.get('/users')
  console.log(res.data)
}

onMounted(()=>{
  getUsers()
})
</script>

<template>
  <div id="app">
    <RouterView />
  </div>
</template>

<style scoped>
#app {
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
  display: flex;
  flex-flow: column nowrap;
}
</style>

1.2. 拦截的几种写法

1.2.1. 完整匹配
import axios from "axios"
import { mock } from 'mockjs'

mock('http://localhost:3000/users', {
    "status": 200,
    "message": "success",
    "data|3-10": [
        {
            "id|+1": 1,
            "name": "@name",
            "age|18-30": 1,
            "address": "@county(true)"
        }
    ]
})

const getUsers =  async () => {
  const res = await axios.get('/users')
  console.log(res.data)
}
getUsers()
1.2.2. method
import { mock } from 'mockjs'

mock('http://localhost:3000/users', "get", {
    "status": 200,
    "message": "success",
    "data|3-10": [
        {
            "id|+1": 1,
            "name": "@name",
            "age|18-30": 1,
            "address": "@county(true)"
        }
    ]
})
1.2.3. 正则
import { mock } from 'mockjs'

mock(/.*?\/users/, {
    "status": 200,
    "message": "success",
    "data|3-10": [
        {
            "id|+1": 1,
            "name": "@name",
            "age|18-30": 1,
            "address": "@county(true)"
        }
    ]
})
1.2.4. 函数模式
import { mock } from 'mockjs'
import type { MockjsRequestOptions } from 'mockjs'

mock(/.*?\/users/, "post", function(options: MockjsRequestOptions){
    console.log(options)
    return {
        "status": 200,
        "message": "success",
        "data|3-10": [
            {
                "id|+1": 1,
                "name": "@name",
                "age|18-30": 1,
                "address": "@county(true)"
            }
        ]
    }
})

2. 拦截增删改查接口示例

import {mock} from 'mockjs'
import type { MockjsRequestOptions } from 'mockjs'

mock(/.*?\/users/, "get", {
    "status": 200,
    "message": "success",
    "data": [
        {
            "name": "@name",
            "age|18-30": 1,
            "address": "@county(true)"
        }
    ]
})

mock(/.*?\/users/, "post", function (options: MockjsRequestOptions){
    console.log('post请求', options)
    return {
        "status": 200,
        "message": "添加成功",
        "data": {}
    }
})

mock(/.*?\/users\/\d+/, "put", function (options: MockjsRequestOptions){
    console.log('put请求', options)
    return {
        "status": 200,
        "message": "更新成功",
        "data": {}
    }
})

mock(/.*?\/users\/\d+/, "delete", {
    "status": 200,
    "message": "删除成功",
    "data": {}
})

const getUsers =  async () => {
  let res = await axios.get('/users')
  console.log("get", res.data)
  res = await axios.post('/users', {name: '张三'})
  console.log("post", res.data)
  res = await axios.put('/users/1', {name: '李四'})
  console.log("put", res.data)
  res = await axios.delete('/users/1')
  console.log("delete", res.data)
}
getUsers()

实际应用Vue3 项目中使用 Mock.js

在一个使用 Vue 3 和 TypeScript 的项目中整合 Mock.js 来模拟数据是一个常见的开发实践,尤其是在项目的早期阶段或者当后端接口尚未定义或完成时。这可以帮助前端开发者独立于后端进行开发和测试。

前置条件:创建 Vue 项目

我们开始之前,一定要确保 Vue 项目已创建,并且我们需要安装 axios 与 ts 依赖,如果未创建项目的请参考下面命令创建。

更多关于 TypeScript 请参考:Vue3 引入 TypeScript

# 创建 Vue 项目
npm create vue@latest

# 安装 axios 依赖
npm install axios

# 安装 TS(如果项目中没有的话)
npm install --save-dev typescript
npm install --save-dev @vue/cli-plugin-typescript

步骤 1:安装 Mock.js

首先,打开您的终端,进入您的 Vue 3 项目目录,然后运行以下命令来安装 Mock.js:

npm install mockjs

# 如果是TS项目,则还需执行下面的命令
npm install -D @types/mockjs

步骤 2配置 axios

项目主要的目录结构如下:

src
  api
    index.ts
    users.ts
  json
    users.json
  mocks
    index.ts
    users.ts
  main.ts
  App.vue

封装 axios 并新建用户接口文件:在项目 src 目录下新建 api 文件夹,用来处理接口调用信息。创建一个 index.tsusers.ts 文件来定义您的接口。

src/api/index.ts

import axios from 'axios'

const baseURL = 'http://localhost:3000'
export const axiosInstance = axios.create({
    baseURL: baseURL
})

export interface Response<T> {
    status: number,
    data: T,
    message: string
}

axiosInstance.interceptors.request.use(
  (request) => {
      // request.headers['token'] = `xxx`
      return request
  },
  (error) => {
      return Promise.reject(error)
  }
)
axiosInstance.interceptors.response.use(
  (response) => {
    if (response?.data.status === 200) {
      return response?.data.data
    }
  },
  (error) => {
      console.error('接口错误',error)
      return Promise.reject(error)
  }
)

src/mocks/users.ts

import {axiosInstance} from "@/api/index"
import type {Response} from '@/api/index'

const USER = '/users'

interface UserResponse {
    readonly id: number
    name: string
    age: number
    email?: string
    password?: string
    address?: string
}
export function getUsers():Promise<Response<UserResponse[]>> {
    return axiosInstance.get(USER)
}

步骤 3json-server 模拟真实 API

新建一个 users.json 文件,用于 json-server 启动本地 JSON 服务器测试接口使用。

src/json/users.json

{
  "users": {
    "status": 200,
    "message": "success",
    "data": [
      {
        "id": 1,
        "name": "Joe",
        "age": 30,
        "email": "joe@gmail.com",
        "password": "123456",
        "address": "123 Main St."
      },
      {
        "id": 2,
        "name": "Jane",
        "age": 25,
        "email": "jane@gmail.com",
        "password": "123456",
        "address": "456 Main St."
      },
      {
        "id": 3,
        "name": "John",
        "age": 40,
        "email": "john@gmail.com",
        "password": "123456",
        "address": "789 Main St."
      },
      {
        "id": 4,
        "name": "Jimmy",
        "age": 35,
        "email": "jimmy@gmail.com",
        "password": "123456",
        "address": "321 Main St."
      }
    ]
  }
}

启动 json-server:

json-server -w users.json 

更多信息请参考:json-server

步骤 4:配置 Mock 数据

创建 Mock 文件: 在项目中,通常我们会创建一个专用的目录来存放 Mock 配置和数据。例如,在 src 目录下创建一个 mocks 目录,然后创建一个 index.tsusers.ts 文件来定义您的 Mock 规则。

src/mocks/index.ts

import './users'

src/mocks/users.ts

import {mock} from 'mockjs'

mock(/.*?\/users/, "get", {
    "status": 200,
    "message": "success",
    // 生成3-10个用户数据
    "data|3-10": [
        {
            "id|+1": 1,
            "name": "@cname",
            "age": "@integer(18,30)",
            "email": "@email",
            "password": "@string('number', 6)",
            "address": "@city(true)"
        }
    ]
})

在项目中引入 Mock.js:src/main.ts 文件中引入 Mock.js 配置,以确保它在项目构建时被加载。src/main.ts:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

/**
 * 开发环境下引入 Mock.js
 *   因为是基于vite的项目,所以使用 import.meta.env.VITE_NODE_ENV,基于Vue CLI 的用process.env.NODE_ENV
 *   Mock.js 是异步加载 =》 确保应用在 Mock.js 准备好后启动
 * */
const startApp = async () => {
    if (import.meta.env.VITE_NODE_ENV === 'development') {
        // import '@/mocks' 这是错误的 =》 因为 import 语句在 JavaScript 中是静态的,不能在运行时条件性地执行
        await import('@/mocks')
    }
    app.mount('#app');
}
startApp()

这里使用条件导入(import())来确保只在开发环境中使用 Mock.js,防止在生产环境中引入。

步骤 5:使用 Mock 数据

在 Vue 组件中使用 Mock.js 模拟的 API 数据非常直接。这里在组件 App.vue 来展示如何获取和显示用户数据。

src/App.vue

<script setup lang="ts">
import { RouterView } from 'vue-router'
import {onMounted, ref} from "vue"
import {getUsers} from '@/api/users'

const users = ref([]);
const selectUsers =  async () => {
  let res = await getUsers()
  users.value = res
}

onMounted(()=>{
  selectUsers()
})
</script>

<template>
  <div id="app">
    <div>
      <h1>用户列表</h1>
      <ul>
        <li v-for="user in users" :key="user.id">
          {{ user.name }} - {{ user.age }} years old
        </li>
      </ul>
    </div>
    <RouterView />
  </div>
</template>

在这个组件中,我们使用 Vue 3 的 Composition API,通过 axios 发起 GET 请求到 /api/users。由于已经配置了 Mock.js 来拦截这个请求,所以请求将返回我们预先定义的模拟数据。

步骤 6:运行和测试

运行您的 Vue 应用,您将看到显示几个随机生成的用户信息。这证明 Mock.js 正在正常工作,并成功拦截和返回了模拟数据。

[步骤 7]:优化 _Mock 开关控制

变通一下,我们可以在页面中设置一个开关,当打开这个开关时就 Mock 模拟数据,关闭则取消。

新增 pinia 状态管理,/src/stores/mock.ts

import { ref } from 'vue'
import { defineStore } from 'pinia'

export const useMockStore = defineStore('mock', () => {
  // state =》 ref()
  const isMock = ref(true)

  // actions => function()
  const toggleMock = (value) => {
    isMock.value = value
  }

  return {
    isMock,
    toggleMock
  }
})

src/main.ts

import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import 'vant/lib/index.css'
import { useMockStore } from "@/stores/mock"

const app = createApp(App)

app.use(createPinia())
app.use(router)

/**
 * 开发环境下引入 Mock.js
 *   因为是基于vite的项目,所以使用 import.meta.env.VITE_NODE_ENV,基于Vue CLI 的用process.env.NODE_ENV
 *   Mock.js 是异步加载 =》 确保应用在 Mock.js 准备好后启动
 * */
const startApp = async () => {
    if (import.meta.env.VITE_NODE_ENV === 'development' && useMockStore().isMock) {
        // import '@/mocks' 这是错误的 =》 因为 import 语句在 JavaScript 中是静态的,不能在运行时条件性地执行
        await import('@/mocks')
    }
    app.mount('#app');
}
startApp()

src/App.vue:

<script setup lang="ts">
import { RouterView } from 'vue-router'
import {onMounted, ref} from "vue"
import {getUsers} from '@/api/users'
import {useMockStore} from "@/stores/mock";

const users = ref([])
const {isMock, toggleMock} = useMockStore()
const changeSwitchMock = (value) => {
  toggleMock(value)
  value ? selectUsers() : clearUsers()
}

const clearUsers = () => {
  users.value = []
}
const selectUsers =  async () => {
  let res = await getUsers()
  users.value = res
}

onMounted(()=>{
  isMock ? selectUsers() : ''
})
</script>

<template>
  <div id="app">
      <div class="switch-color">
        <span class="text"> Mock 模拟 </span>
        <van-switch  v-model="isMock"
                     active-color="#07c160"
                     inactive-color="#dcdee0"
                     @change="changeSwitchMock"
        />
        <van-button @click="changeSwitchMock" round icon="replay" plain type="primary" size="small">刷新</van-button>
      </div>
    <div>
      <h1>用户列表</h1>
      <ul>
        <li v-for="user in users" :key="user.id">
          {{ user.name }} - {{ user.age }} years old
        </li>
      </ul>
    </div>
  </div>
</template>

<style scoped>
#app {
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
  display: flex;
  flex-flow: column nowrap;
}

.switch-color {
  display: flex;
  align-content: center;
  .text {
    align-self: center;
  }
  .van-switch {
    align-self: center;
    margin-right:10px;
  }
}
</style>

通过这种方式,使用 Mock.js 在您的 Vue 3 + TypeScript 项目中帮助模拟后端 API,从而可以在后端尚未开发完成的情况下进行前端开发和测试。

实际应用Vue3 项目中 Mock.js 本地模拟增删改查

使用 Mock.js 模拟 RESTful API 的增删改查 (CRUD) 操作是一种非常实用的方法,特别是在后端尚未开发完成时。这不仅可以帮助前端开发和测试,也可以在演示或原型阶段使用。以下是如何使用 Mock.js 来模拟这些操作的具体步骤。

1. 创建项目和初始化

创建 Vue 项目并安装必要的依赖:

# 创建 Vue 项目
npm create vue@latest

npm install axios mockjs
# 如果是TS项目,则还需执行下面的命令
npm install -D @types/mockjs

2. 配置 Mock.js

在项目中创建一个目录结构,用于存放 Mock 数据和配置。

src/
├── api/           						# API 调用函数
│   ├── users.ts    				
├── mocks/         						# Mock.js 配置和数据
│   ├── index.ts    				
│   ├── data   							
|		|		├── users.ts   				# 初始 Mock 数据
│   ├── models   						
|		|		├── User.ts   				# 定义用户数据类型
│   ├── services   					
|		|		├── mockServices.ts 	# Mock.js 配置
└── views/         						# 页面视图

2.1. Mock.js 数据和配置

src/mocks/index.ts

import './services/mockService'

src/mocks/data/users.ts

// 初始 Mock 数据 src/mocks/data/users.ts
import type { User } from '../models/User'

export const users: User[] = [
  {
    id: 'e1aEAEB1-e9C5-46a0-6Bb2-4dcCeeaD1F12',
    name: '陆秀英',
    age: 48,
    email: 'r.abypkt@gmail.com',
    address: '黑龙江省 绥化市 其它区'
  },
  {
    id: '5fd1FD0b-F30b-36fc-6Ec4-5bd163AB47F7',
    name: '顾桂英',
    age: 59,
    email: 'f.muwaj@gmail.com',
    address: '西藏自治区 昌都地区 贡觉县'
  },
  {
    id: '4f05f659-3cEd-6C9f-Ae79-5BBdD4CBd2EE',
    name: '吴丽',
    age: 47,
    email: 'w.icy@gmail.com',
    address: '香港特别行政区 九龙 油尖旺区'
  }
]

src/mocks/models/User.ts

// 定义用户数据类型 src/mocks/models/User.ts
export interface User {
  id: string
  name: string
  age: number
  email?: string
  address?: string
}

src/mocks/services/mockService.ts

// 设置 Mock 接口处理增删改查请求 src/mocks/services/mockService.ts
import { users } from '../data/users'
import type { User } from '../models/User'
import { mock, Random } from 'mockjs'

// 查
mock('/api/users', 'get', () => users)
// 增
mock('/api/users', 'post', (options: any) => {
  const newUser: User = JSON.parse(options.body)
  newUser.id = Random.guid()
  users.push(newUser)
  return { user: newUser }
})
// 删
mock(/\/api\/users\/[0-9a-fA-F\-]+/, 'delete', (options: any) => {
  const id = options.url.split('/').pop()
  const index = users.findIndex((u) => u.id === id)
  users.splice(index, 1)
  return { message: 'User deleted successfully' }
})
// 改
mock(/\/api\/users\/[0-9a-fA-F\-]+/, 'put', (options: any) => {
  const id = options.url.split('/').pop()
  const updates = JSON.parse(options.body)
  const user = users.find((u) => u.id === id)
  // Object.assign(target, ...source objects) 将源对象推入目标对象并显示目标对象
  Object.assign(user, updates)
  return { user }
})

2.2. 主入口引入 mock 配置

src/main.ts 中引入 mock 配置以确保它在应用加载前被加载。

import { createApp } from 'vue'

import App from './App.vue'
import router from './router'
import './mocks'  // 确保 mock 配置被加载

const app = createApp(App)

app.use(router)

app.mount('#app')

3. 创建 API 调用函数

src/api/users.ts

import axios from 'axios'
import type { User } from '@/mocks/models/User'

export const getUsers = () => axios.get('/api/users')
export const addUser = (user: User) => axios.post('/api/users', user)
export const updateUser = (id: string, user: User) => axios.put(`/api/users/${id}`, user)
export const deleteUser = (id: string) => axios.delete(`/api/users/${id}`)

4. 创建和配置 Vue 组件

创建用户管理的 Vue 组件(例如 src/components/UsersComponent.vue):

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { User } from '@/mocks/models/User'
import { mock, Random } from 'mockjs'
import { addUser, deleteUser, getUsers, updateUser } from '@/api/users'

const users = ref<User[]>([])

onMounted(() => {
  selectUsers()
})

const selectUsers = async () => {
  const { data } = await getUsers()
  users.value = data
}

const handleAdd = async () => {
  const user = mock({
    id: Random.guid(),
    name: '@cname',
    email: '@email',
    age: '@integer(18, 50)',
    address: '@city(true)'
  })
  await addUser(user)
  await selectUsers()
}
const handleEdit = async (user: User) => {
  const newUser = mock({
    name: '@cname',
    email: '@email',
    age: '@integer(18, 50)',
    address: '@city(true)'
  })
  await updateUser(user.id, newUser)
  await selectUsers()
}
const handleDelete = async (id: string) => {
  await deleteUser(id)
  await selectUsers()
}
</script>

<template>
  <!--显示用户列表和操作页面-->
  <div>
    <ul v-for="user in users" :key="user.id">
      <li>{{ user.name }} - {{ user.email }}</li>
      <button @click="handleDelete(user.id)">Delete</button>
      <button @click="handleEdit(user)">Edit</button>
    </ul>
    <button @click="handleAdd">Add User</button>
  </div>
</template>

<style scoped></style>

5. 使用组件

在你的主视图或任何其他地方引入并使用这个组件。

总结

通过使用 Mock.js 模拟后端 API,前端开发者可以在后端尚未实现这些接口时独立进行开发和测试。这种方法非常适合快速原型开发和功能演示。通过灵活的 Mock.js 配置,你可以模拟几乎任何类型的数据和行为,有效地支持复杂的前端应用开发。


😸Mock.js 的替代方案

前文中有提到,Mock.js 早在几年之前就不再更新维护,建议非必要不使用它

如果你正在寻找 Mock.js 的替代方案,特别是考虑到 Mock.js 可能不再积极维护,下面几个流行的库,它们可以提供类似或更加先进的功能来模拟 HTTP 请求和响应:

1. Faker.js

Faker

  • 随机性:生成的数据是完全随机的,可能不适合需要具体模式或关联数据的用例。

  • 维护性:虽然库本身维护良好,但可能会有 API 变动的风险。

2. json-server

  • 快速设置:极快速地搭建一个具有 REST API 的服务器。

  • 轻量级:无需复杂配置,一个简单的 JSON 文件即可启动,适合小型或个人项目。

  • 灵活性:可以根据需要轻松定制路由和返回数据。

  • 缺点

  • 不适合大型应用:对于复杂或大规模的后端模拟可能不够强大。

  • 功能限制:没有像真实后端那样的复杂逻辑处理能力。

3. Mirage JS

Mirage JS • An API mocking library for frontend developers

  • 高度可配置,可以模拟复杂的后端逻辑,包括数据库和关系,支持 GraphQL 和 REST API。

  • 集成到现有前端项目中非常方便。

  • 缺点

  • 学习曲线较高。

  • 在浏览器中运行复杂模拟可能影响性能。

4. msw (Mock Service Worker)

Mock Service Worker

  • GitHub 仓库GitHub - mswjs/msw: Seamless REST/GraphQL API mocking library for browser and Node.js.

  • 描述::MSW 是一个使用 Service Worker 在浏览器层面拦截网络请求来模拟 API 的库。它允许你模拟服务器响应,而不需要更改客户端的代码。MSW 适用于浏览器环境和 Node.js。

  • 用途:开发和测试中拦截和修改 API 请求和响应,特别适用于单元和集成测试。

  • ✔️优点

  • 提供接近真实环境的 API 拦截,支持 REST 和 GraphQL API 模拟。

  • 支持浏览器和 Node.js 环境。

  • 缺点

  • 需要理解 Service Workers 的工作原理。

  • 在不支持 Service Workers 的环境中无法使用。

5. Nock

  • 强大的请求拦截和响应能力。

  • 非常适合自动化测试环境。

  • 缺点

  • 仅限于 Node.js,不适用于浏览器。

  • 学习和使用相对复杂。

6. axios-mock-adapter

  • 测试隔离:确保测试不依赖外部服务,提高可靠性和速度。

  • 简单集成:与 axios 直接集成,易于设置和使用。

  • 灵活性:可自定义拦截请求和响应,模拟不同的 HTTP 状态码和响应时间。

  • 缺点

  • 局限性:仅适用于使用 axios 的项目。

  • 功能有限:主要用于简单的响应模拟,不适合复杂后端逻辑模拟。

  • 不适用于生产:设计仅适用于测试环境,不应在生产环境中使用。

每个工具或库都有其特定的优势和最佳应用场景,选择时你可以根据项目需求、开发环境和个人偏好来考虑。