背景介绍
大部分项目有产品经理参与前期的需求及原型制作,但近两年我们组负责的项目没有专职产品经理及UI工程师参与。由于项目已上线正式交付给用户使用,很多新需求不便于直接在ui工程大刀阔斧的改动,毕竟从需求 → 原型 → 前端UI → 后端接口 → 前后端联调 → 正式上线,一般都会有一个不短的周期。直接在前端ui工程跟进需求的话,中间极有可能穿插其他的bug修复及功能调整相关的短期改动需求,导致原型相关的代码跟ui代码混在一起,难免会由于粗心造成一些可大可小的线上事故。
项目分析
针对以上实际情况,最终项目组决定由前端工程师来负责项目的原型工作,原型效果由需求方确认后再着手后续的前后端开发工作。
本着节约工作量考虑,原型工程使用 ui工程相同的代码,那样后期在原型工程的改动也可以很方便的迁移到ui工程。
为达到这个目的,需要将在原型工程请求的后端数据改用 mock 方式。mock 数据有多种可行的实现方式:
- 大部分API管理应用(如Torna、YApi、apizza等)都在提供管理接口的核心功能上带有给接口提供mock数据机制,然后开发时代理到API管理应用的mock路径
- 本地维护一套mock数据,然后起一个mock服务提供接口请求
- 本地维护一套接口json数据,拦截 ajax 请求响应 mock 的数据
以上实现方式都有各自的优缺点:
- 方案1:受制于接口管理平台,大部分接口管理平台不能自定义接口path,那样就会造成mock,路径跟实际路径不一致。而且在不同的平台维护,极容易造成最终的数据不一致的情况。
- 方案2:可使用Node.js自写一套解析json提供mock服务的程序,该方式能最大程度的还原浏览器网络请求过程,要花更多的时间定义json 数据;由于实际使用时存在路径变量的情况,复杂的路由路径跟 mock数据的匹配方式也要花不少时间。若有足够多的时间处理这个路径匹配机制,该方案是最佳,整个前端工程的代码不用调整(那样也更更方便的跟ui工程做代码比对及合并),只需在原型工程代理一个不同的服务路径即可。
- 方案3:有成熟的技术实现方案,能最快的响应现有需求,但需在已有 ui 工程上调整部分网络请求相关的文件。
最终我在实现过程中是选择了第三种,主要是原型工程都是前端工程师维护,相关的mock数据也都在一个平台直接管理是最节省时间的。
实现方案
为实现该方案,在原有 ui 工程的基础上,主要做了开展了如下工作:
1、模拟用户认证,添加 src/core/mock.js
文件,可根据系统实际情况模拟数据即可,核心内容如下:
/**
* 原型工程为跟console-ui整体结构保持一致,为跳过用户认证逻辑,本处添加一些环境初始化相关的数据
*/
export function mockInit() {
// 模拟token 进行
sessionStorage.setItem('token', 'p_RQAnFyIFZtGDS_iZwThGs5z8tWOM0BDSN03z1EWkA.Q0Ayf2dx4aaFfoJzLfS3YiRoqdScvk-OHJn7Kb_lRQU')
// 模拟用户信息
sessionStorage.setItem('userid', 'SELF-DAN4_QiFBwU7CF-AQkPTatwW6tjKV_FjyDDUpkGYSvA')
sessionStorage.setItem('para-token', 'TmdOGodfh75ueoAZsaeznT5qpeZJ_eaAyWJCwcao8Ro-2082234049')
sessionStorage.setItem('userLoginLogId', '2744')
}
export const mockFormResponse = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(null)
}, 1000)
})
}
2、main.js 引入并执行初始化方法:
import { mockInit } from './core/mock'
mockInit()
3、 添加 src/api/mock 目录,然后添加mockData子目录用于管理相关的 mock 数据,基本的目录结构如下图所示:
4、 添加 src/api/mock/index.js 主要用于获取所有 mockData
目录下以 .json
结尾的文件,并提供 mock数据,核心代码如下:
/**
* 用于将 mockData 目录下的所有以.json 文件结尾的mock文件整合为一个,对外提供mock服务
*/
import lodashGet from 'lodash/get'
import { mockFormResponse } from '../../core/mock.js'
const modulesFiles = require.context('./mockData', false, /\.json$/)
const mockDataObj = {}
modulesFiles.keys().map(modulePath => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
mockDataObj[moduleName] = modulesFiles(modulePath)
})
export default {
/**
*
* @param {String} mockPath mock数据路径,一般是「模块名.mock数据对象名」
* @param {Number} timeout 延时时长,单位为ms,默认为1秒
*/
getMockData: (mockPath, timeout) => {
if (!timeout) {
timeout = 1000
}
if (mockPath === 'common.formMock') {
return mockFormResponse()
}
const mockData = lodashGet(mockDataObj, mockPath)
return new Promise((resolve, reject) => {
setTimeout(() => {
if (mockData) {
resolve(mockData)
} else {
reject(new Error('数据暂未定义'))
}
}, timeout)
})
},
mockFormResponse
}
5、改造 Ajax 请求逻辑.本系统使用的 axios
作为 http 库,并且使用层面做了全局封装。而 axios
提供了 adapter 机制可以很方便的实现我们的 mock 场景,核心代码如下:
import axios from 'axios'
import mockService from '../api/mock/index.js'
const service = axios.create({
baseURL: process.env.VUE_APP_GATEWAY_URL, // url = base url + request url
adapter: async(config) => {
// mock数据拦截
if (config.mockCode) {
const data = await mockService.getMockData(config.mockCode)
return new Promise((resolve, reject) => {
resolve({
data,
status: 200
})
})
}
},
// withCredentials: true, send cookies when cross-domain requests
timeout: 60000 // request timeout
})
6、改造所有 api 目录下所有获取数据的方法,在原有基础上统一添加 mockCode
参数。示例如下:
- api 请求方法种添加
mockCode
: - 对应模块的.json mock 文件种添加需响应的数据:
7、改造表格封装组件。为便于表格数据的异步加载,我们基于 el-table
做了些封装,额可以传入一个url 就可方便的分页获取表格数据,这一套机制是不走我们api 目录封装的方法的,故需要在请求时也需要指定 mockCode
,具体示例代码如下图所示:
为适配该场景,在发起请求前如果存在mockCode,则在请求参数种添加mockCode:
总结
通过该工作,扩展了解决问题的多角度思维模式,面对需求只要前期做好规划,梳理出可行的实现方案,最后的技术实现往往是最简单的。
评论 (0)