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 { param2Obj } from '@/utils'
|
||||
|
||||
const List = []
|
||||
const count = 100
|
||||
|
@ -29,8 +28,8 @@ for (let i = 0; i < count; i++) {
|
|||
}
|
||||
|
||||
export default {
|
||||
getList: config => {
|
||||
const { importance, type, title, page = 1, limit = 20, sort } = param2Obj(config.url)
|
||||
'/article/list': config => {
|
||||
const { importance, type, title, page = 1, limit = 20, sort } = config.query
|
||||
|
||||
let mockList = List.filter(item => {
|
||||
if (importance && item.importance !== +importance) return false
|
||||
|
@ -50,21 +49,26 @@ export default {
|
|||
items: pageList
|
||||
}
|
||||
},
|
||||
getPv: () => ({
|
||||
pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
|
||||
}),
|
||||
getArticle: (config) => {
|
||||
const { id } = param2Obj(config.url)
|
||||
'/article/detail': config => {
|
||||
const { id } = config.query
|
||||
for (const article of List) {
|
||||
if (article.id === +id) {
|
||||
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'
|
||||
}),
|
||||
updateArticle: () => ({
|
||||
},
|
||||
'/article/update': {
|
||||
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 = {
|
||||
admin: {
|
||||
roles: ['admin'],
|
||||
|
@ -18,17 +16,18 @@ const userMap = {
|
|||
}
|
||||
|
||||
export default {
|
||||
loginByUsername: config => {
|
||||
const { username } = JSON.parse(config.body)
|
||||
'/login/login': config => {
|
||||
const { username } = config.body
|
||||
return userMap[username]
|
||||
},
|
||||
getUserInfo: config => {
|
||||
const { token } = param2Obj(config.url)
|
||||
'/login/logout': 'success',
|
||||
'/user/info': config => {
|
||||
const { token } = config.query
|
||||
if (userMap[token]) {
|
||||
return userMap[token]
|
||||
} else {
|
||||
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 { param2Obj } from '@/utils'
|
||||
|
||||
const NameList = []
|
||||
const count = 100
|
||||
|
@ -12,12 +11,11 @@ for (let i = 0; i < count; i++) {
|
|||
NameList.push({ name: 'mockPan' })
|
||||
|
||||
export default {
|
||||
searchUser: config => {
|
||||
const { name } = param2Obj(config.url)
|
||||
'/search/user': config => {
|
||||
const { name } = config.query
|
||||
const mockNameList = NameList.filter(item => {
|
||||
const lowerCaseName = item.name.toLowerCase()
|
||||
if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false
|
||||
return true
|
||||
return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0)
|
||||
})
|
||||
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']
|
||||
}]
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build:prod": "vue-cli-service build ",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:sit": "vue-cli-service build --mode text",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"test": "npm run lint",
|
||||
|
|
|
@ -17,12 +17,16 @@ import i18n from './lang' // Internationalization
|
|||
import './icons' // icon
|
||||
import './errorLog' // error log
|
||||
import './permission' // permission control
|
||||
import './mock' // simulation data
|
||||
|
||||
import * as filters from './filters' // global filters
|
||||
|
||||
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, {
|
||||
id: 'UA-109340118-1',
|
||||
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
|
||||
const service = axios.create({
|
||||
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
|
||||
withCredentials: true, // 跨域请求时发送 cookies
|
||||
timeout: 5000 // request timeout
|
||||
})
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict'
|
||||
require('@babel/register')
|
||||
const path = require('path')
|
||||
const bodyParser = require('body-parser')
|
||||
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
|
@ -8,7 +9,28 @@ function resolve(dir) {
|
|||
|
||||
module.exports = {
|
||||
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: {
|
||||
resolve: {
|
||||
|
|
Loading…
Reference in New Issue