From 858b835f367e00788ce09d9ed558d2325c04fe30 Mon Sep 17 00:00:00 2001 From: RoBlues Date: Sun, 2 Feb 2020 13:52:01 +0800 Subject: [PATCH 1/5] =?UTF-8?q?modify=20=E6=8A=8Amock/index.js=E4=B8=AD?= =?UTF-8?q?=E4=B8=8Emock-server=E7=9B=B8=E5=85=B3=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=BD=AC=E7=A7=BB=E5=88=B0mock-server.js=E4=B8=AD?= =?UTF-8?q?=E5=8E=BB=EF=BC=8C=E5=AE=9E=E7=8E=B0=E4=B8=A4=E5=88=99=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/index.js | 16 +--------------- mock/mock-server.js | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/mock/index.js b/mock/index.js index 217ee3d5..196e2927 100644 --- a/mock/index.js +++ b/mock/index.js @@ -54,18 +54,4 @@ export function mockXHR() { } } -// for mock server -const responseFake = (url, type, respond) => { - return { - url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), - type: type || 'get', - response(req, res) { - console.log('request invoke:' + req.path) - res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) - } - } -} - -export default mocks.map(route => { - return responseFake(route.url, route.type, route.response) -}) +export default mocks diff --git a/mock/mock-server.js b/mock/mock-server.js index 4c4cb2af..873d7bc4 100644 --- a/mock/mock-server.js +++ b/mock/mock-server.js @@ -5,14 +5,30 @@ const path = require('path') const mockDir = path.join(process.cwd(), 'mock') +const Mock = require('mockjs') + +/** + * 修订说明:把mock文件夹下面的文件功能进行梳理 + * + * 1. /mock/index.js输出mockXHR()函数,使用改写 + * XMLHttpRequest对象的方式,在浏览器中拦截请求,返回响应 + * + * 2. /mock/mock-server.js引用/mock/index.js中导出的 + * mocks数据(未经responseFake转换),自行加工后作为 + * devServer启动的express应用的路由,从而实现真正的后台 + * api服务 + */ function registerRoutes(app) { let mockLastIndex const { default: mocks } = require('./index.js') - for (const mock of mocks) { + 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(mocks).length + const mockRoutesLength = Object.keys(mocksForServer).length return { mockRoutesLength: mockRoutesLength, mockStartIndex: mockLastIndex - mockRoutesLength @@ -27,6 +43,17 @@ function unregisterRoutes() { }) } +// for mock server +const responseFake = (url, type, respond) => { + return { + url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), + type: type || 'get', + response(req, res) { + console.log('request invoke:' + req.path) + res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) + } + } +} module.exports = app => { // es6 polyfill require('@babel/register') From 4ff36f1d840e9c7086084278364f9d5586030577 Mon Sep 17 00:00:00 2001 From: RoBlues Date: Mon, 3 Feb 2020 12:36:13 +0800 Subject: [PATCH 2/5] change export object of vue.config.js to a function --- vue.config.js | 214 +++++++++++++++++++++++++------------------------- 1 file changed, 108 insertions(+), 106 deletions(-) diff --git a/vue.config.js b/vue.config.js index 1ec990ea..b546968e 100644 --- a/vue.config.js +++ b/vue.config.js @@ -16,113 +16,115 @@ const name = defaultSettings.title || 'vue Element Admin' // page title const port = process.env.port || process.env.npm_config_port || 9527 // dev port // All configuration item explanations can be find in https://cli.vuejs.org/config/ -module.exports = { - /** - * You will need to set publicPath if you plan to deploy your site under a sub path, - * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, - * then publicPath should be set to "/bar/". - * In most cases please use '/' !!! - * Detail: https://cli.vuejs.org/config/#publicpath - */ - publicPath: '/', - outputDir: 'dist', - assetsDir: 'static', - lintOnSave: process.env.NODE_ENV === 'development', - productionSourceMap: false, - devServer: { - port: port, - open: true, - overlay: { - warnings: false, - errors: true +module.exports = () => { + return { + /** + * You will need to set publicPath if you plan to deploy your site under a sub path, + * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, + * then publicPath should be set to "/bar/". + * In most cases please use '/' !!! + * Detail: https://cli.vuejs.org/config/#publicpath + */ + publicPath: '/', + outputDir: 'dist', + assetsDir: 'static', + lintOnSave: process.env.NODE_ENV === 'development', + productionSourceMap: false, + devServer: { + port: port, + open: true, + overlay: { + warnings: false, + errors: true + }, + before: require('./mock/mock-server.js').default }, - before: require('./mock/mock-server.js') - }, - configureWebpack: { - // provide the app's title in webpack's name field, so that - // it can be accessed in index.html to inject the correct title. - name: name, - resolve: { - alias: { - '@': resolve('src') - } - } - }, - chainWebpack(config) { - config.plugins.delete('preload') // TODO: need test - config.plugins.delete('prefetch') // TODO: need test - - // set svg-sprite-loader - config.module - .rule('svg') - .exclude.add(resolve('src/icons')) - .end() - config.module - .rule('icons') - .test(/\.svg$/) - .include.add(resolve('src/icons')) - .end() - .use('svg-sprite-loader') - .loader('svg-sprite-loader') - .options({ - symbolId: 'icon-[name]' - }) - .end() - - // set preserveWhitespace - config.module - .rule('vue') - .use('vue-loader') - .loader('vue-loader') - .tap(options => { - options.compilerOptions.preserveWhitespace = true - return options - }) - .end() - - config - // https://webpack.js.org/configuration/devtool/#development - .when(process.env.NODE_ENV === 'development', - config => config.devtool('cheap-source-map') - ) - - config - .when(process.env.NODE_ENV !== 'development', - config => { - config - .plugin('ScriptExtHtmlWebpackPlugin') - .after('html') - .use('script-ext-html-webpack-plugin', [{ - // `runtime` must same as runtimeChunk name. default is `runtime` - inline: /runtime\..*\.js$/ - }]) - .end() - config - .optimization.splitChunks({ - chunks: 'all', - cacheGroups: { - libs: { - name: 'chunk-libs', - test: /[\\/]node_modules[\\/]/, - priority: 10, - chunks: 'initial' // only package third parties that are initially dependent - }, - elementUI: { - name: 'chunk-elementUI', // split elementUI into a single package - priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app - test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm - }, - commons: { - name: 'chunk-commons', - test: resolve('src/components'), // can customize your rules - minChunks: 3, // minimum common number - priority: 5, - reuseExistingChunk: true - } - } - }) - config.optimization.runtimeChunk('single') + configureWebpack: { + // provide the app's title in webpack's name field, so that + // it can be accessed in index.html to inject the correct title. + name: name, + resolve: { + alias: { + '@': resolve('src') } - ) + } + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + // set preserveWhitespace + config.module + .rule('vue') + .use('vue-loader') + .loader('vue-loader') + .tap(options => { + options.compilerOptions.preserveWhitespace = true + return options + }) + .end() + + config + // https://webpack.js.org/configuration/devtool/#development + .when(process.env.NODE_ENV === 'development', + config => config.devtool('cheap-source-map') + ) + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + } + ) + } } } From 0a4b206c410e17bcf09462e641bc1c74a8876094 Mon Sep 17 00:00:00 2001 From: RoBlues Date: Mon, 3 Feb 2020 19:01:09 +0800 Subject: [PATCH 3/5] move option for enable/disable mock/mock-erver to .env.* file --- .env.development | 13 +++++++++++++ .env.production | 11 +++++++++++ mock/mock-server.js | 5 ++++- src/main.js | 5 ++++- vue.config.js | 37 ++++++++++++++++++++++++++++++++++++- 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/.env.development b/.env.development index 8f5856db..02e2f5a7 100644 --- a/.env.development +++ b/.env.development @@ -4,6 +4,18 @@ ENV = 'development' # base api VUE_APP_BASE_API = '/dev-api' +# 用VUE_APP_MOCK配置如何使用Mock +# 该值会被用在Vue.config.js和src/main.js中 +# 其取值和意义如下: +# None - 表示不使用Mock或Mock-server,不配置意义相同 +# Mock - 表示使用mock +# Mock_Server 表示使用Mock_Server。 +VUE_APP_MOCK = Mock + +# Mock_Server Context 表示在使用Mock-Server时,它的应用context。 +# 该值会被用在vue.config.js和/mock/mock-server.js中 +VUE_APP_MOCK_SERVER_CONTEXT = /mock + # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, # to control whether the babel-plugin-dynamic-import-node plugin is enabled. # It only does one thing by converting all import() to require(). @@ -12,3 +24,4 @@ VUE_APP_BASE_API = '/dev-api' # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js VUE_CLI_BABEL_TRANSPILE_MODULES = true + diff --git a/.env.production b/.env.production index 80c81030..be814328 100644 --- a/.env.production +++ b/.env.production @@ -4,3 +4,14 @@ ENV = 'production' # base api VUE_APP_BASE_API = '/prod-api' +# 用VUE_APP_MOCK配置如何使用Mock +# 该值会被用在Vue.config.js和src/main.js中 +# 其取值和意义如下: +# None - 表示不使用Mock或Mock-server,不配置意义相同 +# Mock - 表示使用mock +# Mock_Server 表示使用Mock_Server。 +VUE_APP_MOCK = Mock + +# Mock_Server Context 表示在使用Mock-Server时,它的应用context。 +# 该值会被用在vue.config.js和/mock/mock-server.js中 +VUE_APP_MOCK_SERVER_CONTEXT = /mock \ No newline at end of file diff --git a/mock/mock-server.js b/mock/mock-server.js index 873d7bc4..63b395d2 100644 --- a/mock/mock-server.js +++ b/mock/mock-server.js @@ -45,8 +45,11 @@ function unregisterRoutes() { // for mock server const responseFake = (url, type, respond) => { + /** Mock-Server的Context */ + const MOCK_SERVER_CONTEXT = '^' + process.env.VUE_APP_MOCK_SERVER_CONTEXT + return { - url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), + url: new RegExp(`${MOCK_SERVER_CONTEXT}${url}`), type: type || 'get', response(req, res) { console.log('request invoke:' + req.path) diff --git a/src/main.js b/src/main.js index 1049d762..249f7a32 100644 --- a/src/main.js +++ b/src/main.js @@ -27,7 +27,10 @@ import * as filters from './filters' // global filters * Currently MockJs will be used in the production environment, * please remove it before going online ! ! ! */ -if (process.env.NODE_ENV === 'production') { +/** + * VUE_APP_MOCK is defined in .env.production or .env.development + */ +if (process.env.VUE_APP_MOCK === 'Mock') { const { mockXHR } = require('../mock') mockXHR() } diff --git a/vue.config.js b/vue.config.js index b546968e..771fb581 100644 --- a/vue.config.js +++ b/vue.config.js @@ -15,6 +15,36 @@ const name = defaultSettings.title || 'vue Element Admin' // page title // port = 9527 npm run dev OR npm run dev --port = 9527 const port = process.env.port || process.env.npm_config_port || 9527 // dev port +/** mock-server开关,该环境变量定义在文件`.env.development`中*/ +const enableMockServer = process.env.VUE_APP_MOCK === 'Mock_Server' +const mockServerContext = process.env.VUE_APP_MOCK_SERVER_CONTEXT +const mockProxy = { + /** + * dev-api/login => mock/login + * 本对象是express的代理插件http-proxy-middlewaredre + * (https://github.com/chimurai/http-proxy-middleware) + * 当方法是POST时,数据无法从原始请求传到代理请求,见下帖 + * https://github.com/chimurai/http-proxy-middleware/issues/40 + * 解决办法是在它的onProxyReq事件中,手动把请求的body数据搬运到代理请求中 + * + * target是指匹配该路径的主机。见https://webpack.js.org/configuration/dev-server/#devserverproxy + */ + [process.env.VUE_APP_BASE_API]: { + target: `http://localhost:${port}`, + changeOrigin: true, + ws: true, // proxy websockets + pathRewrite: { ['^' + process.env.VUE_APP_BASE_API]: mockServerContext }, + /** 在POST方法下,直接把body数据以JSON形式搬运到代理请求对象proxyRequest中去 */ + onProxyReq: (proxyRequest, request, response, options) => { + if (request.method === 'POST' && request.body) { + const bodyData = JSON.stringify(request.body) + proxyRequest.setHeader('Content-Type', 'application/json') + proxyRequest.setHeader('Content-Length', Buffer.byteLength(bodyData)) + proxyRequest.write(bodyData) + } + } + }} + // All configuration item explanations can be find in https://cli.vuejs.org/config/ module.exports = () => { return { @@ -37,7 +67,12 @@ module.exports = () => { warnings: false, errors: true }, - before: require('./mock/mock-server.js').default + + proxy: enableMockServer ? mockProxy : undefined, + /** 启动mock服务器 */ + /** @link https://webpack.js.org/configuration/dev-server/#devserverbefore*/ + /** before定义为function (app)。其中app指的是express的app */ + before: enableMockServer ? require('./mock/mock-server.js').default : undefined }, configureWebpack: { // provide the app's title in webpack's name field, so that From efcd50cfeafefc2ab40e08d6b9471b0fe0265453 Mon Sep 17 00:00:00 2001 From: RoBlues Date: Mon, 3 Feb 2020 19:42:50 +0800 Subject: [PATCH 4/5] restore mock/mock-server.js --- mock/mock-server.js | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 mock/mock-server.js diff --git a/mock/mock-server.js b/mock/mock-server.js new file mode 100644 index 00000000..806fdacc --- /dev/null +++ b/mock/mock-server.js @@ -0,0 +1,84 @@ +const chokidar = require('chokidar') +const bodyParser = require('body-parser') +const chalk = require('chalk') +const path = require('path') +const Mock = require('mockjs') + +const mockDir = path.join(process.cwd(), 'mock') + +function registerRoutes(app) { + let mockLastIndex + 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 + } +} + +function unregisterRoutes() { + Object.keys(require.cache).forEach(i => { + if (i.includes(mockDir)) { + delete require.cache[require.resolve(i)] + } + }) +} + +// for mock server +const responseFake = (url, type, respond) => { + return { + url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), + type: type || 'get', + response(req, res) { + console.log('request invoke:' + req.path) + res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) + } + } +} + +module.exports = app => { + // es6 polyfill + require('@babel/register') + + // parse app.body + // https://expressjs.com/en/4x/api.html#req.body + app.use(bodyParser.json()) + app.use(bodyParser.urlencoded({ + extended: true + })) + + const mockRoutes = registerRoutes(app) + var mockRoutesLength = mockRoutes.mockRoutesLength + var mockStartIndex = mockRoutes.mockStartIndex + + // watch files, hot reload mock server + 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) + + // clear routes cache + 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)) + } + } + }) +} From 30d0ff0d44c4bd2bc5fbd7b158aef4c9707ffb45 Mon Sep 17 00:00:00 2001 From: RoBlues Date: Mon, 3 Feb 2020 19:45:42 +0800 Subject: [PATCH 5/5] restore mock/mock-server.js --- mock/mock-server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mock/mock-server.js b/mock/mock-server.js index 806fdacc..9de466ff 100644 --- a/mock/mock-server.js +++ b/mock/mock-server.js @@ -33,8 +33,10 @@ function unregisterRoutes() { // for mock server const responseFake = (url, type, respond) => { + /** Mock-Server的Context */ + const MOCK_SERVER_CONTEXT = '^' + process.env.VUE_APP_MOCK_SERVER_CONTEXT return { - url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), + url: new RegExp(`${MOCK_SERVER_CONTEXT}${url}`), type: type || 'get', response(req, res) { console.log('request invoke:' + req.path)