refactor(mock): mak mock api compatible with both web-view and webpack server
1. 把 Mockjs 功能移到 server 端中间件,同时也兼容前端直接劫持 XHR 2. dev 环境下默认作为 express 中间件通过 webpack server 提供 mock api 3. prod 构建时,默认在前端用 Mockjs 劫持 XHR benefits: - dev 开发调试时能直接看到 XHR 请求,方便调试网络,能和后端对接联调 - 避开在开发时因为 Mockjs 引起的网络 bug - prod 构建时劫持 XHR,保证本项目的 Github Pages preview 能正常显示 (逻辑和 error-log 一样) - 前后台使用的 mock 是同一份代码,不会增加维护负担 ref: [#562](https://github.com/PanJiaChen/vue-element-admin/issues/562#issuecomment-378116233)
This commit is contained in:
parent
1dd0acd1e8
commit
330dc7e921
|
@ -1 +1 @@
|
||||||
VUE_APP_BASE_API = 'https://api-dev'
|
VUE_APP_BASE_API = '/api'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
VUE_APP_BASE_API = 'https://api-prod'
|
VUE_APP_BASE_API = '/api'
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Mock from 'mockjs'
|
import Mock from 'mockjs'
|
||||||
import { param2Obj } from '@/utils'
|
|
||||||
|
|
||||||
const List = []
|
const List = []
|
||||||
const count = 100
|
const count = 100
|
||||||
|
@ -29,8 +28,8 @@ for (let i = 0; i < count; i++) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getList: config => {
|
'/article/list': config => {
|
||||||
const { importance, type, title, page = 1, limit = 20, sort } = param2Obj(config.url)
|
const { importance, type, title, page = 1, limit = 20, sort } = config.query
|
||||||
|
|
||||||
let mockList = List.filter(item => {
|
let mockList = List.filter(item => {
|
||||||
if (importance && item.importance !== +importance) return false
|
if (importance && item.importance !== +importance) return false
|
||||||
|
@ -50,21 +49,26 @@ export default {
|
||||||
items: pageList
|
items: pageList
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPv: () => ({
|
'/article/detail': config => {
|
||||||
pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
|
const { id } = config.query
|
||||||
}),
|
|
||||||
getArticle: (config) => {
|
|
||||||
const { id } = param2Obj(config.url)
|
|
||||||
for (const article of List) {
|
for (const article of List) {
|
||||||
if (article.id === +id) {
|
if (article.id === +id) {
|
||||||
return article
|
return article
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createArticle: () => ({
|
'/article/pv': {
|
||||||
|
pvData: [
|
||||||
|
{ key: 'PC', pv: 1024 },
|
||||||
|
{ key: 'mobile', pv: 1024 },
|
||||||
|
{ key: 'ios', pv: 1024 },
|
||||||
|
{ key: 'android', pv: 1024 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'/article/create': {
|
||||||
data: 'success'
|
data: 'success'
|
||||||
}),
|
},
|
||||||
updateArticle: () => ({
|
'/article/update': {
|
||||||
data: 'success'
|
data: 'success'
|
||||||
})
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
import Mock from 'mockjs'
|
||||||
|
import mocks from './mocks'
|
||||||
|
import { param2Obj } from '../src/utils'
|
||||||
|
|
||||||
|
const MOCK_API_BASE = '/mock'
|
||||||
|
|
||||||
|
export function mockXHR() {
|
||||||
|
// 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题
|
||||||
|
// https://github.com/nuysoft/Mock/issues/300
|
||||||
|
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
|
||||||
|
}
|
||||||
|
this.proxy_send(...arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
function XHR2ExpressReqWrap(respond) {
|
||||||
|
return function(options) {
|
||||||
|
let result = null
|
||||||
|
if (respond instanceof Function) {
|
||||||
|
const { body, type, url } = options
|
||||||
|
// https://expressjs.com/en/4x/api.html#req
|
||||||
|
result = respond({
|
||||||
|
method: type,
|
||||||
|
body: JSON.parse(body),
|
||||||
|
query: param2Obj(url)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result = respond
|
||||||
|
}
|
||||||
|
return Mock.mock(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [route, respond] of Object.entries(mocks)) {
|
||||||
|
Mock.mock(new RegExp(`${route}`), XHR2ExpressReqWrap(respond))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseFake = (route, respond) => (
|
||||||
|
{
|
||||||
|
route: new RegExp(`${MOCK_API_BASE}${route}`),
|
||||||
|
response(req, res) {
|
||||||
|
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Object.keys(mocks).map(route => responseFake(route, mocks[route]))
|
|
@ -1,5 +1,3 @@
|
||||||
import { param2Obj } from '@/utils'
|
|
||||||
|
|
||||||
const userMap = {
|
const userMap = {
|
||||||
admin: {
|
admin: {
|
||||||
roles: ['admin'],
|
roles: ['admin'],
|
||||||
|
@ -18,17 +16,18 @@ const userMap = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
loginByUsername: config => {
|
'/login/login': config => {
|
||||||
const { username } = JSON.parse(config.body)
|
const { username } = config.body
|
||||||
return userMap[username]
|
return userMap[username]
|
||||||
},
|
},
|
||||||
getUserInfo: config => {
|
'/login/logout': 'success',
|
||||||
const { token } = param2Obj(config.url)
|
'/user/info': config => {
|
||||||
|
const { token } = config.query
|
||||||
if (userMap[token]) {
|
if (userMap[token]) {
|
||||||
return userMap[token]
|
return userMap[token]
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
|
||||||
logout: () => 'success'
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import login from './login'
|
||||||
|
import article from './article'
|
||||||
|
import search from './remoteSearch'
|
||||||
|
import transaction from './transaction'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...login,
|
||||||
|
...article,
|
||||||
|
...search,
|
||||||
|
...transaction
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Mock from 'mockjs'
|
import Mock from 'mockjs'
|
||||||
import { param2Obj } from '@/utils'
|
|
||||||
|
|
||||||
const NameList = []
|
const NameList = []
|
||||||
const count = 100
|
const count = 100
|
||||||
|
@ -12,12 +11,11 @@ for (let i = 0; i < count; i++) {
|
||||||
NameList.push({ name: 'mockPan' })
|
NameList.push({ name: 'mockPan' })
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
searchUser: config => {
|
'/search/user': config => {
|
||||||
const { name } = param2Obj(config.url)
|
const { name } = config.query
|
||||||
const mockNameList = NameList.filter(item => {
|
const mockNameList = NameList.filter(item => {
|
||||||
const lowerCaseName = item.name.toLowerCase()
|
const lowerCaseName = item.name.toLowerCase()
|
||||||
if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false
|
return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0)
|
||||||
return true
|
|
||||||
})
|
})
|
||||||
return { items: mockNameList }
|
return { items: mockNameList }
|
||||||
}
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Mock from 'mockjs'
|
||||||
|
|
||||||
|
const count = 20
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'/transaction/list': {
|
||||||
|
total: count,
|
||||||
|
[`items|${count}`]: [{
|
||||||
|
order_no: '@guid()',
|
||||||
|
timestamp: +Mock.Random.date('T'),
|
||||||
|
username: '@name()',
|
||||||
|
price: '@float(1000, 15000, 0, 2)',
|
||||||
|
'status|1': ['success', 'pending']
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,12 +17,16 @@ import i18n from './lang' // Internationalization
|
||||||
import './icons' // icon
|
import './icons' // icon
|
||||||
import './errorLog' // error log
|
import './errorLog' // error log
|
||||||
import './permission' // permission control
|
import './permission' // permission control
|
||||||
import './mock' // simulation data
|
|
||||||
|
|
||||||
import * as filters from './filters' // global filters
|
import * as filters from './filters' // global filters
|
||||||
|
|
||||||
import VueAnalytics from 'vue-analytics'
|
import VueAnalytics from 'vue-analytics'
|
||||||
|
|
||||||
|
import { mockXHR } from '../mock' // simulation data
|
||||||
|
|
||||||
|
// mock api in github pages site build
|
||||||
|
if (process.env.NODE_ENV === 'production') { mockXHR() }
|
||||||
|
|
||||||
Vue.use(VueAnalytics, {
|
Vue.use(VueAnalytics, {
|
||||||
id: 'UA-109340118-1',
|
id: 'UA-109340118-1',
|
||||||
router
|
router
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import Mock from 'mockjs'
|
|
||||||
import loginAPI from './login'
|
|
||||||
import articleAPI from './article'
|
|
||||||
import remoteSearchAPI from './remoteSearch'
|
|
||||||
import transactionAPI from './transaction'
|
|
||||||
|
|
||||||
// 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题
|
|
||||||
// https://github.com/nuysoft/Mock/issues/300
|
|
||||||
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
|
|
||||||
}
|
|
||||||
this.proxy_send(...arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock.setup({
|
|
||||||
// timeout: '350-600'
|
|
||||||
// })
|
|
||||||
|
|
||||||
// 登录相关
|
|
||||||
Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
|
|
||||||
Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
|
|
||||||
Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
|
|
||||||
|
|
||||||
// 文章相关
|
|
||||||
Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
|
|
||||||
Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)
|
|
||||||
Mock.mock(/\/article\/pv/, 'get', articleAPI.getPv)
|
|
||||||
Mock.mock(/\/article\/create/, 'post', articleAPI.createArticle)
|
|
||||||
Mock.mock(/\/article\/update/, 'post', articleAPI.updateArticle)
|
|
||||||
|
|
||||||
// 搜索相关
|
|
||||||
Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser)
|
|
||||||
|
|
||||||
// 账单相关
|
|
||||||
Mock.mock(/\/transaction\/list/, 'get', transactionAPI.getList)
|
|
||||||
|
|
||||||
export default Mock
|
|
|
@ -1,23 +0,0 @@
|
||||||
import Mock from 'mockjs'
|
|
||||||
|
|
||||||
const List = []
|
|
||||||
const count = 20
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
List.push(Mock.mock({
|
|
||||||
order_no: '@guid()',
|
|
||||||
timestamp: +Mock.Random.date('T'),
|
|
||||||
username: '@name()',
|
|
||||||
price: '@float(1000, 15000, 0, 2)',
|
|
||||||
'status|1': ['success', 'pending']
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getList: () => {
|
|
||||||
return {
|
|
||||||
total: List.length,
|
|
||||||
items: List
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ import { getToken } from '@/utils/auth'
|
||||||
// create an axios instance
|
// create an axios instance
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
|
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
|
||||||
|
withCredentials: true, // 跨域请求时发送 cookies
|
||||||
timeout: 5000 // request timeout
|
timeout: 5000 // request timeout
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
require('@babel/register')
|
require('@babel/register')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
function resolve(dir) {
|
function resolve(dir) {
|
||||||
return path.join(__dirname, dir)
|
return path.join(__dirname, dir)
|
||||||
|
@ -8,7 +9,28 @@ function resolve(dir) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
devServer: {
|
devServer: {
|
||||||
open: true
|
open: true,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8080/mock',
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: {
|
||||||
|
'^/api': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
after(app) {
|
||||||
|
// parse app.body
|
||||||
|
// http://expressjs.com/en/4x/api.html#req.body
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }))
|
||||||
|
|
||||||
|
// import ES2015 module from common.js module
|
||||||
|
const { default: mocks } = require('./mock')
|
||||||
|
for (const mock of mocks) {
|
||||||
|
app.all(mock.route, mock.response)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|
Loading…
Reference in New Issue