在前端进行开发的时候,若后端还未开发完成,前端也可以通过 mockjs 自己手动模拟后端接口返回的随机数据。
官方介绍:
Mock.js (opens new window)可以 生成随机数据,拦截 Ajax 请求
提示
关于 mock-server.js 和 vite-plugin-mock 两种方案的使用示例,可以查看个人项目:
https://github.com/Lencamo/mock-programme
# 一、Mock.js
优点:
- 解决了很好的解决了数据模拟问题
缺点:
- 在浏览器上不能真正的模拟请求与响应过程
network 中没有发出任何的请求、本地调试只能通过 console.log
# 1、安装配置
安装:
npm install mockjs -D
npm install axios
配置:
新建文件并编写内容:
- mock / index.js
import Mock from 'mockjs'
- main.js
import './mock/index.js'
# 2、mock 语法
Mock 语法的内容,在其官方文档中写得是非常详细的:
更加便捷的是:当我们处于官方示例页面中时,可以打开控制台,随意地试验 mock 方法。
使用示例:
- mock / index.js
import Mock from 'mockjs'
const { newsList } = Mock.mock({
'newsList|20-35': [
{
id: '@increment(1)',
title: '@ctitle()',
content: '@cparagraph(5,10)',
img_url: "@image('50*50','#FF83FA','#FCFCFC','png','mono')",
add_time: '@date(yyyy-MM-dd hh:mm:ss)'
}
]
})
// 模拟返回新闻列表接口数据
Mock.mock('/api/news', 'get', () => {
return {
status: 200,
message: '获取新闻列表成功',
list: newsList,
total: newsList.length
}
})
# 3、请求拦截
关于拦截 ajax 请求部分的官方文档(即 Mock.js 0.1 文档),在 mock 官方地址的文档部分可以查看到详细内容:
Mock.mock( rurl?, rtype?, template|function(options) )
各个参数的含义及其默认值:
参数 | 默认值 | 说明 |
---|---|---|
rurl | 可选 | 可以是 URL 字符串或 URL 正则 |
rtype | 可选 | 可以是 GET、POST、PUT、DELETE 等 |
template | 可选 | 可以是对象或字符串 |
options | 本次请求的 Ajax 选项集 |
下面列举几种使用情况:
① 根据数据模板生成模拟数据
前面学习 mock 语法的示例代码使用的就是这个
Mock.mock(template)
② 拦截到匹配 rurl 的 Ajax 请求时,并根据数据模板 template 生成模拟数据
Mock.mock(rurl, rtype, template)
③ 当拦截到匹配 rurl 的 Ajax 请求时,函数 function(options) 将被执行,并把执行结果作为响应数据返回
Mock.mock( rurl, rtype, function(options) )
# 4、开发应用
https://www.bilibili.com/video/BV1v741197q2/ (55 分钟左右)
安装:
npm install mockjs -D
npm install axios
配置:
// main.js
// 使用mockjs模拟接口数据
import './mock/index.js'
文件结构:
query 参数:path-parser.js
// 手动🤔获取res.query参数
var getQuery = (url, name) => {
console.log(url, name)
const index = url.indexOf('?')
if (index !== -1) {
const queryStrArr = url.substr(index + 1).split('&')
for (var i = 0; i < queryStrArr.length; i++) {
const itemArr = queryStrArr[i].split('=')
console.log(itemArr)
if (itemArr[0] === name) {
return itemArr[1]
}
}
}
return null
}
├── mock
├── index.js
├── utils
│ └── path-parser.js
└── modules # api管理
├── news.js
└── products.js
具体使用:
获取数据
- 获取分页数据
// /api/news?pageinde=1&pagesize=10
Mock.mock(/\/api\/news/, 'get', (option) => {
// console.log(option)
const pageIndex = getQuery(option.url, 'pageIndex')
const pageSize = getQuery(option.url, 'pageSize')
const start = (pageIndex - 1) * pageSize
const end = pageIndex * pageSize
const totalPage = Math.ceil(newList.length / pageSize)
// 获取指定的数据
const list = pageIndex > totalPage ? [] : newsList.slice(start, end)
return {
status: 200,
message: '获取新闻列表成功',
list: list,
total: newList.length
}
})
- 使用接口
getNewsList(){
axios.get('/api/get/news',{
params: {
pageInex: 1,
pageSize: 10
}
}).then(res => {
this.list = res.data.list
})
}
添加数据
- 添加新闻
Mock.mock('/api/news', 'post', (options) => {
const body = JSON.parse(options.body)
// console.log(body)
// 添加数据操作
newsList.push(
Mock.mock({
id: '@increment()',
title: body.title,
content: body.content,
img_url: "@image('100x100','#31363f', '#00405d', '#FFF', 'png','任先生')",
add_time: '@date(yyyy-MM-dd)'
})
)
// 响应内容
return {
status: 200,
message: '添加成功',
list: newsList,
total: newsList.length
}
})
- 使用接口
addNews() {
axios.post('/api/news',{
title: this.title,
content: this.content
}).then(res=>{
console.log(res)
})
}
删除数据
- 删除新闻
Mock.mock('/api/news', 'delete', (options) => {
const body = JSON.parse(options.body)
// console.log(body)
// 删除数据操作
const index = newsList.findIndex((item) => item.id === body.id)
newList.splice(index, 1)
// 响应内容
return {
status: 200,
message: '删除成功',
list: newsList,
total: newsList.length
}
})
- 使用接口
removeNews(id) {
axios.delete('/api/news',{
id
}).then(res=>{
console.log(res)
})
}
# 二、mock-server.js
- 一点点的 webpack 知识
- 一点点的 node 知识
# 1、极简版
安装:
npm install mockjs -D
npm install axios
配置:
webpack3 中:devServer.before (opens new window)、webpack 新版 中:devServer.onBeforeSetupMiddleware (opens new window)
// vue.config.js
module.exports = {
devServer: {
before: require('./mock/index.js') // 引入mock/index.js
},
lintOnSave: false
}
使用示例
- 新建:.env.development
MOCK = true
- 新建:mock / index.js
最好对其进行模块化划分
const fs = require('fs')
const path = require('path')
const Mock = require('mockjs') // mockjs 导入依赖模块
const bodyParser = require('body-parser')
// 读取json文件
function getJsonFile(filePath) {
// 读取指定json文件
var json = fs.readFileSync(path.resolve(__dirname, filePath), 'utf-8')
// 解析并返回
return JSON.parse(json)
}
function checkToken(token) {
if (!token || token === 'null' || token === 'undefind') return false
if (
token ===
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE1MTI1NDQyOTksImV4cCI6MTUxMjYzMDY5OX0.eGrsrvwHm-tPsO9r_pxHIQ5i5L1kX9RX444uwnRGaIM'
) {
return true
}
return false
}
// 返回一个函数
module.exports = function (app) {
if (process.env.MOCK == 'true') {
app.use(bodyParser.json()) //数据JSON类型
// 监听http请求
app.post('/login', function (req, res) {
// console.log(req.body);
// 每次响应请求时读取mock data的json文件
let username = req.body.username
let password = req.body.password
let json = ''
if (username === 'admin' && password === '123456') {
// getJsonFile方法定义了如何读取json文件并解析成数据对象
json = getJsonFile('./userInfo1.json')
} else if (username === 'zhangsan' && password === '123456') {
json = getJsonFile('./userInfo2.json')
} else {
json = getJsonFile('./error.json')
}
// 将json传入 Mock.mock 方法中,生成的数据返回给浏览器
res.json(Mock.mock(json))
})
app.get('/users', function (req, res) {
let token = req.headers.authorization
if (!checkToken(token)) {
let errorJson = getJsonFile('./tokenerror.json')
res.json(Mock.mock(errorJson))
return
}
let json = getJsonFile('./userlist.json')
res.json(Mock.mock(json))
})
app.delete('/users/:id', function (req, res) {
let json = getJsonFile('./success.json')
res.json(Mock.mock(json))
})
app.get('/goods', function (req, res) {
let json = getJsonFile('./goods.json')
res.json(Mock.mock(json))
})
}
}
# 2、基本概述
开源项目 vue-element-admin (opens new window) 的方案
mock-server 是我在 vue-element-admin (opens new window) 中看到的一种解决方案。
优点:它会在本地会启动一个 mock-server 来模拟数据,线上环境还是继续使用 mockjs 来进行模拟
实现原理
mock 是完全基于 webpack-dev-serve (opens new window) 来实现的,所以在你启动前端服务的同时,mock-server 就会自动启动,而且这里还通过 chokidar (opens new window) 来观察 mock 文件夹内容的变化。在发生变化时会清除之前注册的 mock-api 接口,重新动态挂载新的接口,从而支持热更新。
由于是一个真正的 server,所以你可以通过控制台中的 network,清楚的知道接口返回的数据结构。并且同时解决了之前 mockjs 会重写 XMLHttpRequest 对象,导致很多第三方库失效的问题。
参与 mock-server 的相关文件分布:
├── mock
├── index.js
├── mock-core # 核心代码
│ ├── utils.js
│ ├── mock-XHR.js # --> mock.js封装
│ └── mock-server.js # --> mock-server封装
└── modules # api管理
├── cart.js
└── products.js
核心代码:mock-server.js
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const Mock = require('mockjs')
const mockDir = path.join(process.cwd(), 'mock')
function registerRoutes(app) {
let mockLastIndex
const { mocks } = require('./../index.js')
const mocksForServer = mocks.map((route) => {
return responseFake(route.url, route.type, route.response)
})
for (const mock of mocksForServer) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocksForServer).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach((i) => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
}
})
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
type: type || 'get',
response(req, res) {
console.log('request invoke:' + req.path)
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
}
}
}
module.exports = (devServer) => {
// const { app } = devServer
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
devServer.app.use(bodyParser.json())
devServer.app.use(
bodyParser.urlencoded({
extended: true
})
)
const mockRoutes = registerRoutes(devServer.app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar
.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
})
.on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
// remove mock routes stack
devServer.app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(devServer.app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
}
}
})
}
# 3、安装配置
安装:
npm install mockjs [email protected] [email protected] -D
npm install axios
配置:
- .env.development
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = '/dev-api'
① 本地使用 mock-server,线上使用 mockjs
- vue.config.js
// 开启:【本地】mock-server模拟
devServer: {
port: port,
open: true,
client: {
overlay: {
errors: true,
warnings: false,
runtimeErrors: true
}
},
onBeforeSetupMiddleware: require('./mock/mock-core/mock-server.js')
},
- main.js
// 开启:【线上】使用mock.js模拟
import { mockXHR } from '../mock/mock-core/mock-XHR.js'
if (process.env.NODE_ENV === 'production') {
mockXHR()
}
② 本地、线上都使用 mockjs
- main.js
// 完全使用mock.js模拟
import { mockXHR } from '../mock/mock-core/mock-XHR.js'
mockXHR()
# 4、使用示例
- mock / index.js
const role = require('./api-mock/role')
const mocks = [...role]
- mock / modules / users.js
代码示例
// 请求获取的数据(当再次请求时,数据会回到起点)
const Mock = require('mockjs')
const { userList } = Mock.mock({
'userList|35': [
{
'role|1': ['user', 'teacher', 'super-admin', 'root', 'oj-admin'],
name: '@cname()',
classes: '软件@string(number,1,2).@string(number,1,2)本',
studentId: '@id()',
email: '@email()',
uuid: '@guid()'
}
]
})
module.exports = [
// 用户列表请求处理
{
url: '/user/userList',
type: 'get',
response: (config) => {
const { page, size } = config.query
const newsList = userList.filter(
(item, index) => index < size * page && index >= size * (page - 1)
)
return {
code: 200,
msg: '获取列表成功!',
data: newsList,
total: userList.length
}
}
},
// 添加用户请求处理
{
url: '/user/addUser',
type: 'post',
response: (config) => {
const { role, name, classes, studentId, email } = config.body
const newUser = Mock.mock({
role: role,
name: name,
classes: classes ? classes : '空',
studentId: studentId ? studentId : '空',
email: email,
uuid: '@guid()'
})
userList.push(newUser)
return {
code: 200,
msg: '添加用户成功!',
data: userList,
total: userList.length
}
}
},
// 删除用户请求处理
{
url: '/user/delUser',
type: 'post',
response: (config) => {
const { id } = config.body
userList.splice(id, 1)
return {
code: 200,
msg: '删除用户成功!',
data: userList,
total: userList.length
}
}
},
// 编辑某个用户信息请求处理
{
url: '/user/editUser',
type: 'post',
response: (config) => {
const { id, userForm } = config.body
const userObj = []
userObj.push(userForm)
const userDetail = userList.splice(id, 1, userObj[0])
return {
code: 200,
msg: '更改用户信息成功!',
data: userDetail,
total: userList.length
}
}
},
// 更改用户角色信息请求处理
{
url: '/user/userRoleChange',
type: 'post',
response: (config) => {
const { id, role } = config.body
const userDetail = userList.slice(id, id + 1)
// console.log(userDetail)
userDetail[0].role = role
return {
code: 200,
msg: '更改用户角色成功!'
}
}
},
// 批量添加用户请求处理
{
url: '/user/addMoreUser',
type: 'post',
response: (config) => {
const addUserDetailArr = config.body
// console.log('测试信息')
userList.push.apply(userList, addUserDetailArr)
return {
code: 200,
msg: '批量添加用户成功!',
data: userList,
total: userList.length
}
}
}
]
# 三、vite-plugin-mock
这个示例项目,可以用于
vite
环境下的数据模拟(mock)
# 1、基本概述
vite-plugin-mock 是我在 vue-vben-admin (opens new window) 中看到的一种用于模拟数据的方案。
根据vite-plugin-mock (opens new window)的描述,它支持本地环境和生产环境,Connect 服务中间件在本地使用,mockjs 在生产环境中使用。
[!WARNING] 经过我的亲身测试,vite-plugin-mock 在开发环境下可以到达 mock-server 一样的效果。但生产环境下表现并不如意,官方自己也说了,生产环境下不支持 header 的获取、RESTful 格式参数获取、mock 文件不要使用 node 模块等。除此之外,vite-plugin-mock 在使用时还不可以设置全局的 baseURL(得用代理解决)。并且关于生产环境的相关 issues,官方也没有进行相关 fix。
# 2、文件分析
vite-plugin-mock 和前面的 mock-server 使用基本一致,没有什么大的变动。
├── mock
├── index.js
├── mock-core # 核心代码
│ ├── utils.js
│ └── mock-XHR.js # --> mock.js封装
└── modules # api管理
├── cart.js
└── products.js
# 3、安装配置
安装:
npm install mockjs vite-plugin-mock -D
npm install axios
配置:
- .env.development
ENV = 'development'
# base api
VITE_BASE_API = '/mock-api'
① 本地使用 vite-plugin-mock
- vite.config.js
import { viteMockServe } from 'vite-plugin-mock'
// https://vitejs.dev/config/
export default defineConfig({
server: {
// 注意:不同于mock-server,此处必须要通过代理去掉 VITE_BASE_API(大大的无语)
proxy: {
'/mock-api': {
target: 'http://127.0.0.1:5174/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/mock\-api/, '')
}
}
},
plugins: [
// 开启:【本地】vite-plugin-mock模拟
viteMockServe({
mockPath: 'mock',
enable: process.env.NODE_ENV === 'development',
watchFiles: false
})
]
})
② 线上使用 mockjs
- main.js
// 开启:【线上】使用mock.js模拟
import { mockXHR } from '../mock/mock-core/mock-XHR'
if (import.meta.env.PROD) {
mockXHR()
}