本文共 4413 字,大约阅读时间需要 14 分钟。
MOCK
是指模拟服务器按照一定的规则或者设定的数据,对客户端的请求进行响应。换个说法就是可以不用搭建后台服务器,就可以实现前端对数据的请求的响应。在vue-element-admin
框架中采用的是MockJS
。
MockJS
的原理是拦截了所有的请求并代理到本地,然后进行数据模拟,所以你会发现 network 中没有发出任何的请求。不过在vue-element-admin
框架中是利用webpack-dev-serve
来实现的,在你启动前端服务的同时,mock-server
就会自动启动,而且这里还通过 chokidar
来观察 mock
文件夹内容的变化.在Vue.config.js
中可以看到有如下配置:
devServer: { port: port, open: true, overlay: { warnings: false, errors: true }, //通过这一行来实现在webpack-dev-serve启动之前启动mock-server服务的 before: require('./mock/mock-server.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')//获取mock静态返回数据所在的目录const mockDir = path.join(process.cwd(), 'mock')module.exports = app => { require('@babel/register') //添加插件 app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) //注册数据响应路由 const mockRoutes = registerRoutes(app) var mockRoutesLength = mockRoutes.mockRoutesLength var mockStartIndex = mockRoutes.mockStartIndex //监测mock目录下的文件变化 chokidar.watch(mockDir, { ignored: /mock-server/, ignoreInitial: true }).on('all', (event, path) => { //一旦发生文件的变化或添加则重新添加响应路由 if (event === 'change' || event === 'add') { try { // remove mock routes stack app._router.stack.splice(mockStartIndex, mockRoutesLength) // 清楚路由缓存数据 unregisterRoutes() //重新注册 const mockRoutes = registerRoutes(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)) } } })}
我们重点来看看这个响应理由是如何被注册的:
function registerRoutes(app) { let mockLastIndex //这里就是返回的是MockXHR,即利用 const { default: 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 }}const responseFake = (url, type, respond) => { return { //这里就将url上下文前缀拼接在一起 url: new RegExp(`${ process.env.VUE_APP_BASE_API}${ url}`), //默认get方法 type: type || 'get', response(req, res) { console.log('request invoke:' + req.path) //写回json数据 // Mock.mock(templ)即返回templ数据 res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) } }}
虽然代码表面使用的是MockJs
,但是实际上用的却是封装过的MockXHR
,这个封装过程如下:
//import Mock from 'mockjs'import { param2Obj } from '../src/utils'import user from './user'import role from './role'import article from './article'import search from './remote-search'//模拟数据const mocks = [ ...user, ...role, ...article, ...search]export function mockXHR() { //这里重写了Mock的代理方法,重新定义XMLHttpRequest //这里将XHR指向了webpack-dev-serve的XHR Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send Mock.XHR.prototype.send = function() { if (this.custom.xhr) { this.custom.xhr.withCredentials = this.withCredentials || false if (this.responseType) { this.custom.xhr.responseType = this.responseType } } this.proxy_send(...arguments) } function XHR2ExpressReqWrap(respond) { return function(options) { let result = null if (respond instanceof Function) { const { body, type, url } = options result = respond({ method: type, body: JSON.parse(body), query: param2Obj(url) }) } else { result = respond } return Mock.mock(result) } } for (const i of mocks) { Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) }}export default mocks
通过重新包装MockJS
之后,调用的是原生的send
函数,所以在network
中也可以看得见数据的传输。
Mock数据编写在了Mock
目录下的user
、role
、article
、remote-search
文件中,通过上述文件统一引入mocks的数组中。
const mocks = [ ...user, ...role, ...article, ...search]
然后通过遍历数组绑定在了Mock的内部映射表中。
for (const i of mocks) { Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))}
转载地址:http://lqfen.baihongyu.com/