3、数据模拟:Mock.js 数据模拟、接口封装(Axios)、Promise 与 async/await 异步处理
做可视化大屏,最头疼的是什么?
我个人觉得,不是图表画不出来,而是——数据呢?
后端接口还没写好,前端怎么开发?总不能干等着吧。所以这一章,我们就来解决这个核心问题:在没有真实接口的情况下,如何让前端先跑起来。
3.1 为什么需要 Mock 数据?
我在项目中遇到过好几次这种情况:
产品说「大屏下周要演示」,后端说「接口还在联调」。你想想看,前端总不能对着空页面干瞪眼吧?
所以 Mock 数据就派上用场了。说白了,就是模拟后端返回的数据,让前端能独立开发、独立调试。
3.2 Mock.js 快速上手
Mock.js 是个好东西。它不仅能生成随机数据,还能拦截 Ajax 请求。我习惯把它用在开发环境,等后端接口就绪后,一键切换。
安装与引入
npm install mockjs --save-dev
然后在项目中创建一个 mock 文件夹,专门放模拟数据。
基本用法
// mock/data.js
import Mock from 'mockjs'
const data = Mock.mock({
'list|10': [{
'id|+1': 1,
'name': '@cname',
'age|18-60': 1,
'city': '@city',
'salary|5000-20000': 1
}]
})
export default data
这里有几个常用的占位符:
| 占位符 | 说明 | 示例结果 |
|---|---|---|
| @cname | 随机中文姓名 | 张三 |
| @city | 随机城市 | 北京 |
| @date | 随机日期 | 2024-03-15 |
| @integer(1, 100) | 随机整数 | 42 |
| @float(0, 100, 2, 2) | 随机浮点数 | 67.89 |
/[A-Z][a-z]{5}/ 可以生成随机英文单词。我在模拟设备编号时经常用这个。
拦截接口请求
这才是 Mock.js 的真正威力——它可以直接拦截 Axios 发起的请求。
// mock/index.js
import Mock from 'mockjs'
Mock.mock('/api/user/list', 'get', {
code: 200,
message: 'success',
'data|20': [{
'id|+1': 1,
'name': '@cname',
'age|20-50': 1,
'department': '@ctitle(2,4)'
}]
})
这样,当 Axios 请求 /api/user/list 时,Mock.js 会自动拦截并返回模拟数据。前端完全感知不到区别。
3.3 Axios 接口封装
直接在每个组件里写 Axios 请求?那可不行。我见过太多项目,接口散落在各个组件里,改个 baseURL 要改几十个文件。
所以,统一封装是必须的。
创建 Axios 实例
// utils/request.js
import axios from 'axios'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 可以在这里加 token
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
// 统一错误处理
console.error('接口异常:', res.message)
return Promise.reject(new Error(res.message))
}
return res
},
error => {
console.error('网络错误:', error.message)
return Promise.reject(error)
}
)
export default service
封装 API 模块
// api/user.js
import request from '@/utils/request'
export function getUserList(params) {
return request({
url: '/api/user/list',
method: 'get',
params
})
}
export function addUser(data) {
return request({
url: '/api/user/add',
method: 'post',
data
})
}
export function deleteUser(id) {
return request({
url: `/api/user/delete/${id}`,
method: 'delete'
})
}
这样做的好处是:接口变更时,只需要改一个文件。我在一个老项目里接手时,发现接口散落在 30 多个组件里,改起来简直想哭。后来花了一周全部重构,从此清爽多了。
3.4 Promise 与 async/await
说到异步处理,就绕不开 Promise。但说实话,Promise 的链式调用写多了,回调地狱是没了,但「链式地狱」又来了。
Promise 基础
Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。状态一旦改变,就不会再变。
// 基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const data = { name: '张三' }
resolve(data)
// 如果出错:reject(new Error('出错了'))
}, 1000)
})
promise.then(data => {
console.log(data) // { name: '张三' }
}).catch(err => {
console.error(err)
})
async/await 语法糖
我个人强烈推荐用 async/await。它让异步代码看起来像同步代码,可读性高太多了。
// 用 async/await 改写
async function fetchUserData() {
try {
const response = await getUserList({ page: 1, size: 10 })
console.log('用户数据:', response.data)
return response.data
} catch (error) {
console.error('获取用户数据失败:', error)
throw error
}
}
实际场景:大屏数据加载
大屏项目通常需要同时加载多个接口。比如同时获取用户数据、销售数据、地图数据。这时候用 Promise.all 最合适。
async function loadDashboardData() {
try {
const [userData, salesData, mapData] = await Promise.all([
getUserList({ page: 1, size: 100 }),
getSalesData({ date: '2024-03-15' }),
getMapData({ region: '全国' })
])
return {
users: userData.data,
sales: salesData.data,
map: mapData.data
}
} catch (error) {
console.error('大屏数据加载失败:', error)
// 可以在这里显示错误提示
showErrorToast('数据加载异常,请稍后重试')
}
}
为什么要用 Promise.all?因为这三个接口互不依赖,可以并行请求。如果串行请求,总耗时就是三个接口耗时之和;并行请求,耗时只等于最慢的那个接口。
3.5 完整流程串联
好了,我们把 Mock、Axios、async/await 串起来,看看完整的开发流程:
- 定义接口文档:和后端约定好接口地址、请求方式、返回数据结构
- 编写 Mock 数据:按照接口文档,用 Mock.js 生成模拟数据
- 封装 Axios:创建请求实例,配置拦截器
- 编写 API 模块:每个接口对应一个函数,返回 Promise
- 在组件中调用:用 async/await 获取数据,渲染到图表
- 联调切换:后端接口就绪后,关闭 Mock,直接请求真实接口
这个流程我在多个大屏项目里验证过,非常稳定。你想想看,前端不用等后端,后端不用催前端,各干各的,效率自然就上来了。
下一章,我们会把这些数据真正渲染到图表上。准备好了吗?