Skip to content

Commit

Permalink
refactor(mock): mak mock api compatible with both web-view and webpac…
Browse files Browse the repository at this point in the history
…k 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](#562 (comment))
  • Loading branch information
zthxxx committed Oct 21, 2018
1 parent 1dd0acd commit 330dc7e
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .env.development
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VUE_APP_BASE_API = 'https://api-dev'
VUE_APP_BASE_API = '/api'
2 changes: 1 addition & 1 deletion .env.production
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VUE_APP_BASE_API = 'https://api-prod'
VUE_APP_BASE_API = '/api'
28 changes: 16 additions & 12 deletions src/mock/article.js → mock/article.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Mock from 'mockjs'
import { param2Obj } from '@/utils'

const List = []
const count = 100
Expand Down Expand Up @@ -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
Expand All @@ -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'
})
}
}
50 changes: 50 additions & 0 deletions mock/index.js
Original file line number Diff line number Diff line change
@@ -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]))
15 changes: 7 additions & 8 deletions src/mock/login.js → mock/login.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { param2Obj } from '@/utils'

const userMap = {
admin: {
roles: ['admin'],
Expand All @@ -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'
}
}

12 changes: 12 additions & 0 deletions mock/mocks.js
Original file line number Diff line number Diff line change
@@ -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
}

8 changes: 3 additions & 5 deletions src/mock/remoteSearch.js → mock/remoteSearch.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Mock from 'mockjs'
import { param2Obj } from '@/utils'

const NameList = []
const count = 100
Expand All @@ -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 }
}
Expand Down
16 changes: 16 additions & 0 deletions mock/transaction.js
Original file line number Diff line number Diff line change
@@ -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']
}]
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 0 additions & 39 deletions src/mock/index.js

This file was deleted.

23 changes: 0 additions & 23 deletions src/mock/transaction.js

This file was deleted.

1 change: 1 addition & 0 deletions src/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
})

Expand Down
24 changes: 23 additions & 1 deletion vue.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
'use strict'
require('@babel/register')
const path = require('path')
const bodyParser = require('body-parser')

function resolve(dir) {
return path.join(__dirname, 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: {
Expand Down

0 comments on commit 330dc7e

Please sign in to comment.