如果想要了解mock模擬資料的話,請看這篇文章(https://www.cnblogs.com/tugenhua0707/p/9813122.html)
在實際應用場景中,總感覺mock資料比較麻煩,並且在webpack3中mock資料的程式碼在正式環境並不能自動移除掉,導致正式環境有mock相對應的程式碼,但是在webpack4中,會使用 tree shaking外掛會自動移除掉未使用到的程式碼的,想要了解tree shaking(https://www.cnblogs.com/tugenhua0707/p/9671618.html),請看這篇文章。所以為了相容webpack版本的話,研究了一種新的方式來模擬後臺介面資料,使用webpack中的 devServer.before這個配置即可,詳細請看如下介紹。
在講解如何模擬資料之前,我們來看下我目前整個專案架構如下:
### 目錄結構如下: demo1 # 工程名 | |--- dist # 打包後生成的目錄檔案 | |--- node_modules # 所有的依賴包 | |--- app | | |---index | | | |-- views # 存放所有vue頁面檔案 | | | | |-- parent.vue # 父元件 | | | | |-- child.vue # 子元件 | | | | |-- index.vue | | | |-- components # 存放vue公用的元件 | | | |-- js # 存放js檔案的 | | | |-- store # store倉庫 | | | | |--- actions.js | | | | |--- mutations.js | | | | |--- state.js | | | | |--- mutations-types.js | | | | |--- index.js | | | |-- app.js # vue入口配置檔案 | | | |-- router.js # 路由配置檔案 | |--- views | | |-- index.html # html檔案 | |--- json # 存放所有模擬資料 | | |-- parent_getPower.json | | |-- parent_reConfig.json | | |-- parent_reconlist.json | | |-- parent_reGroup.json | |--- mock.js # mock 資料的所有呼叫方法 | |--- webpack.config.js # webpack配置檔案 | |--- .gitignore | |--- README.md | |--- package.json | |--- .babelrc # babel轉碼檔案
專案的結構如上所示:其中 json 資料夾內會存放所有模擬的資料,比如和開發後臺約定好的資料,我可以直接把資料複製到json檔案內,比如我們簡單的看下 parent_reConfig.json 資料程式碼如下(假如開發介面返回的資料是這樣的):
{ "data": [{ "bTableName": "reconfig", "businessCode": "reconfig", "businessImportImpl": "reconfig", "businessImportMethod": "reconfig", "businessName": "reconfig" } ], "code": 0 }
其他的json檔案也是類似的資料,這裡不一一貼程式碼哦,有興趣的話,可以到下面的github上檢視demo。
json檔案命名方式:比如我上面json下的叫 parent_reConfig.json, 因為vue頁面叫 parent.vue, 所以字首就是頁面的名稱,然後下劃線(_) + reConfig + 'json', 其中reConfig 是介面的最後一個名字,這樣命名的話,我一眼就可以知道是那個頁面下的介面,介面是做什麼使用的。方便以後介面很多更容易區分。
2. 在專案的根目錄下 mock.js 程式碼是如下所示:
const getPower = require('./json/parent_getPower.json'); const reConfig = require('./json/parent_reConfig.json'); const reConList = require('./json/parent_reconlist.json'); const reGroup = require('./json/parent_reGroup.json'); function Mock(app) { app.get('/xxxx/yyy', function(req, res) { console.log('getPower111'); res.json(getPower); }); app.post('/reconfig', function(req, res) { console.log('reConfig111'); res.json(reConfig); }); app.post('/conlist', function(req, res) { console.log('reConList111'); res.json(reConList); }); app.post('/regroup', function(req, res) { console.log('reGroup111'); res.json(reGroup); }); } module.exports = Mock;
如上程式碼首先是把 json資料夾下的所有的json檔案 require進來,然後定義一個函式,裡面寫所有的(get或post)方法,然後使用 res.json 方式就可以把上面的json資料模擬返回回來。如上的app引數是從webpack.config.js 配置傳進來的。
3. webpack.config.js 配置方式如下:
// 引入mock.js const Mock = require('./mock.js'); module.exports = { devServer: { port: 8082, host: '0.0.0.0', headers: { 'X-foo': '112233' }, inline: true, overlay: true, stats: 'errors-only', before: function(app) { console.log(app); if (process.env.NODE_ENV === 'mock') { Mock(app); } } }, plugins: [ // 設定環境變數資訊 new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) } }) ] }
如上配置程式碼,首先引入 mock.js 進來,上面的mock.js對外返回函式,因此在devServer.before中配置呼叫下Mock函式,傳入引數是app,並且環境變數也判斷了下,如果是 mock環境,就執行mock函式程式碼的資料,否則不是mock命令的話,就不會執行mock資料,而是執行真正環境的資料。下面我們來看下 上面 before函式列印的app是什麼東西,如下所示:
{ [EventEmitter: app] domain: undefined, _events: { mount: [Function: onmount] }, _maxListeners: undefined, setMaxListeners: [Function: setMaxListeners], getMaxListeners: [Function: getMaxListeners], emit: [Function: emit], addListener: [Function: addListener], on: [Function: addListener], prependListener: [Function: prependListener], once: [Function: once], prependOnceListener: [Function: prependOnceListener], removeListener: [Function: removeListener], removeAllListeners: [Function: removeAllListeners], listeners: [Function: listeners], listenerCount: [Function: listenerCount], eventNames: [Function: eventNames], init: [Function: init], defaultConfiguration: [Function: defaultConfiguration], lazyrouter: [Function: lazyrouter], handle: [Function: handle], use: [Function: use], route: [Function: route], engine: [Function: engine], param: [Function: param], set: [Function: set], path: [Function: path], enabled: [Function: enabled], disabled: [Function: disabled], enable: [Function: enable], disable: [Function: disable], acl: [Function], bind: [Function], checkout: [Function], connect: [Function], copy: [Function], delete: [Function], get: [Function], head: [Function], link: [Function], lock: [Function], 'm-search': [Function], merge: [Function], mkactivity: [Function], mkcalendar: [Function], mkcol: [Function], move: [Function], notify: [Function], options: [Function], patch: [Function], post: [Function], propfind: [Function], proppatch: [Function], purge: [Function], put: [Function], rebind: [Function], report: [Function], search: [Function], subscribe: [Function], trace: [Function], unbind: [Function], unlink: [Function], unlock: [Function], unsubscribe: [Function], all: [Function: all], del: [Function], render: [Function: render], listen: [Function: listen], request: IncomingMessage { app: [Circular] }, response: ServerResponse { app: [Circular] }, cache: {}, engines: {}, settings: {}, _eventsCount: 1, locals: { settings: {}}, mountpath: '/', _router: {} }
更多關於 devServer.before可以看官網(https://webpack.js.org/configuration/dev-server/#devserver-before)。
4. package.json 配置如下:
{ "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --progress --colors --devtool cheap-module-eval-source-map --hot --inline", "build": "cross-env NODE_ENV=production webpack --progress --colors --devtool cheap-module-source-map", "build:dll": "webpack --config webpack.dll.config.js", "start": "webpack-dev-server --progress --colors --devtool cheap-module-eval-source-map --hot --inline", "mock": "cross-env NODE_ENV=mock npm run start" }, }
5. app/index/views/parent.vue 程式碼如下所示:
<template> <div> </div> </template> <script type="text/javascript"> import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { const obj = { 'xx': 11 }; // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', obj])]).then((res) => { console.log('getPower'); console.log(typeof res); console.log(res); }); Promise.all([this.commonActionPost(['reConfig', obj])]).then((res) => { console.log('reConfig'); console.log(res); }); Promise.all([this.commonActionPost(['reConList', obj])]).then((res) => { console.log('reConList'); console.log(res); }); Promise.all([this.commonActionPost(['reGroup', obj])]).then((res) => { console.log('reGroup'); console.log(res); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
如上程式碼,當我執行打包命令 npm run mock 時,就會呼叫webpack中的 devServer.before函式,該函式會判斷當前的環境是否是mock命令,如果是mock命令就執行 mock.js中的Mock函式,然後會請求資料。
注意:上面的請求資料是:'//0.0.0.0:8082/reconfig' 這樣的,這樣就可以把 json/parent_reConfig.json請求的資料返回回來,但是我們真正的介面的字首可能是 '//xxx.abc.com', 因此我們在配置所有的介面名稱的時候,介面域名的字首我們可以定義一個變數,然後傳遞進去。比如我下面是這樣定義的:
// const prefix = '//xxx.abc.com'; // 真正的域名介面先註釋掉
const prefix = '//0.0.0.0:8082';
我們可以簡單的判斷下:
let prefix; if (process.env.NODE_ENV === 'mock') { prefix = '//0.0.0.0:8082'; // 測試域名 } else { prefix = '//xxx.abc.com'; // 正式域名 }
const url = prefix + '/reconfig';
類似上面這中形式,因此當我們在頁面上請求 this.$http.get(url);的時候,實際上在mock資料下請求的是 '//0.0.0.0:8082/reconfig' 這樣的,因此會直接被 devServer.before中攔截到,因此會返回我們的模擬上的資料,但是在我們使用 npm run dev 或 npm run build 的時候,我們需要把 //xxx.abc.com 這個真正的介面域名字首開啟,'//0.0.0.0:8082' 需要被註釋掉。就可以了,其他的vue中的程式碼不需要做任何改動,以前該怎麼寫,現在也還是就那麼寫。