關於mockjs的優點,官網這樣描述它:
1)可以前後端分離。
2)增加單元測試的真實性(通過隨機資料,模擬各種場景)。
3)開發無侵入(不需要修改既有程式碼,就可以攔截 Ajax 請求,返回模擬的響應資料。)
4)用法簡單。
5)資料型別豐富(支援生成隨機的文字、數字、布林值、日期、郵箱、連結、圖片、顏色等)。
6)方便擴充套件(支援支援擴充套件更多資料型別,支援自定義函式和正則。)
總之:在開發中並不依賴後端的介面,我們自己根據介面文件,在真實的請求上攔截ajax後,且根據mock的資料規則,使用mock隨機生成和真實類似結構的資料。這樣我們就可以使用這些資料來調式對應的介面。編寫我們前端業務邏輯程式碼。
在學習之前,我們專案的目錄結構如下:
### 目錄結構如下: 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檔案 | |--- webpack.config.js # webpack配置檔案 | |--- .gitignore | |--- README.md | |--- package.json | |--- .babelrc # babel轉碼檔案
一:mock安裝和使用
進入專案的根目錄下,執行mock安裝命令如下:
npm i -D mockjs
使用:
1. 比如生成列表資料,基本程式碼(在vue中演示)
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; export default { data() { return { } }, created() { }, methods: { }, mounted() { const d = Mock.mock({ /* 屬性 list 的值是一個陣列,其中含有 1 到 10 個元素, 屬性 id 是一個自增數(key),起始值為 1,每次增 1 陣列裡面的數量是隨機的,1-10箇中隨機生成幾個 */ 'list|1-10': [ { 'id|+1': 1 } ] }); // "list": [{ "id": 1 }, { "id": 2 }] console.log(d); } } </script>
具體的mock資料含義對應的文件,可以看這個對應的API文件。這裡就不講解基本語法了。
Mock 資料的語法如下:
Mock.mock(rurl?, rtype?, template|function(options))
引數rurl: [可選],表示需要攔截的url,可以是url字串或url正則。
引數rtype: [可選],表示需要攔截的ajax請求的型別,比如 GET、POST、PUT、DELETE、等。
引數 template: [可選],表示資料模板,可以是物件或字串。比如:{ 'data|1-10': [{}]}, '@EMAIL'.
引數 function(options) [可選],表示用於生成響應資料的函式。
引數options 指向本次請求的ajax資料。
Mock.mock()(提供五種引數格式以及語法規範的使用)
1. Mock.mock(template),跟進資料模板生成模擬資料。 如下demo程式碼:
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; export default { data() { return { } }, created() { }, methods: { }, mounted() { var t = { 'title': 'kongzhi', /* 通過重複 a 生成一個字串,重複次數大於等於1,小於等於10 */ 'string1|1-10': 'a', /* 通過重複b生成一個字串,重複的次數等於3 */ 'string2|3': 'b', /* 屬性值自動加1,初始值為100 */ 'number1|+1': 100, /* 生成一個大於等於1,小於等於100的整數,屬性值100只用來確定型別 */ 'number2|1-100': 100, /* 生成一個浮點數,整數部分大於等於1,小於等於100,小數部分保留1到10位 */ 'number3|1-100.1-10': 1, /* 生成一個浮點數,整數部分未123,小數部分1-10位 */ 'number4|123.1-10': 1, /* 生成一個浮點數,整數部分為123,小數部分3位 */ 'number5|123.3': 1, /* 生成一個浮點數,整數部分未123,小數部分為 10位 */ 'number6|123.10': 1.123, /* 隨機生成一個布林值,值為true的概率為1/2, 值為false的概率是 1/2 */ 'boolean1|1': true, /* 隨機生成一個布林值,值為true的概率是 1/(1+2), 值為false的概率是 2/(1+2) */ 'boolean2|1-2': true, /* 生成一個新物件,物件裡面的屬性最小是2位,最大是4位 */ 'object1|2-4': { '110000': '北京市', '120000': '天津市', '130000': '河北省', '140000': '山西省' }, /* 生成一個新物件,物件裡面的屬性是2位 */ 'object2|2': { '310000': '上海市', '320000': '江蘇省', '330000': '浙江省', '340000': '安微省' }, /* 從屬性值 ['AMD', 'CMD', 'UMD'] 中隨機選取1個元素,最為最終值 */ 'array1|1': ['AMD', 'CMD', 'UMD'], /* 通過重複屬性值 Mock.js生成一個新陣列,重複的次數大於等於1,小於等於10 */ 'array2|1-10': ['Mock.js'], /* 通過重複屬性值 Mock.js, 生成一個新陣列,重複的次數為3 */ 'array3|3': ['Mock.js'], /* 執行函式 function(){}, 該函式返回值作為最終的屬性值 */ 'function': function() { return this.title } }; var d = Mock.mock(t); console.log(d); } } </script>
輸出結果如下所示:
2. Mock.mock(rurl, template)
記錄資料模板,當攔截到匹配 rurl的ajax請求時,將根據資料模板 template生成模擬資料,並作為響應資料返回。
如下demo程式碼:
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { Mock.mock('//xxx.abc.com/xxxx/yyy', { 'code': 0, 'list|1-10': [{ 'id|+1': 1, 'email': '@EMAIL' }] }); // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', {}])]).then((res) => { console.log(res); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
如上請求後,列印出 console.log(res); 後,列印出如下圖所示:
3. Mock.mock(rurl, function(options))
記錄用於生成響應資料的函式,當攔截匹配到 rurl的Ajax請求時,函式 function(options)將被執行,並把執行結果作為響應資料返回。
如下程式碼演示:
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { Mock.mock('//xxx.abc.com/xxxx/yyy', function(options){ return options; }); // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', {}])]).then((res) => { console.log(res); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
列印 console.log(res) 結果如下:
4. Mock.mock(rurl, rtype, template)
記錄資料模板,當攔截到匹配 rurl和rtype的ajax請求時,將根據資料模板template生成模擬資料,並作為響應資料返回。
如下程式碼演示:
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { Mock.mock('//xxx.abc.com/xxxx/yyy', 'get', { 'list': [{ 'name': 'kongzhi' }] }); // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', {}])]).then((res) => { console.log(res); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
如下圖所示:
5. Mock.mock(rurl, rtype, function(options))
記錄用於生成響應資料的函式。當攔截到匹配 rurl 和 rtype的ajax請求時,函式 function(options) 將被執行,並把執行結果作為響應資料返回。
如下程式碼演示:
<template> <div> </div> </template> <script type="text/javascript"> import Mock from 'mockjs'; import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { Mock.mock('//xxx.abc.com/xxxx/yyy', 'get', function(options) { return options; }); // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', {}])]).then((res) => { console.log(res); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
如下圖所示:
二:mock資料如何區分開發環境和正式環境?
如上程式碼雖然可以解決資料模擬,但是上面的程式碼有一個明顯的缺點,就是在程式碼內部,如果介面正常的話,那麼我們需要把mock相關的模擬程式碼刪除,如果是一個介面倒是好,但是如果頁面上有多個介面,並且一個專案對應多個單頁面的話,那麼這種重複的工作就顯示非常麻煩,並且很不友好。
因此我這邊想這樣處理,在 app/index/ 下新建一個資料夾叫json資料夾,然後json資料夾裡面放所有模擬相關的json檔案,那麼json檔案的命名如何命名呢?比如一個pageA頁面有多個介面獲取資料,那麼如果我直接以介面的名稱命名的話,那麼過了一段時間後,我開啟json檔案,一眼看過去也分不清哪個json介面資料是那個頁面的,因此我這邊想這樣約定下 json檔名稱命名規範:頁面模組名_介面名稱.json, 比如我vue頁面叫pageA頁面,然後pageA頁面有好多介面,比如其中一個介面是獲取產品名稱介面,假如介面名稱叫 getProdName, 那麼我想json命名就叫 pageA_getProdName.json. 這樣的話,如果以後有多個json的話,一眼就可以看出是那個頁面的介面,並且該介面的作用是幹什麼用的。
app/index/json/parent_yyyy.json 這樣定義,在parent頁面內部,有個介面名稱叫 yyyy, 因此json檔案
命名就叫 parent_yyyy.json; 假如介面返回的json資料如下:
{ "data": [{ "assignerId": 55, "assignerName": "仲宣", "auditStatus": 20, "auditTime": "2018-05-08 16:56:02", "borrowerIdCard": "130204199812050027", "borrowerName": "張小紅", "borrowerSign": "電信白名單使用者", "id": 1, "loanAmount": 3500, "loanPeriod": "12", "loanRate": 21.5, "loanTime": "2018-05-07 19:27:17", "loanUsage": "購物消費", "occurDate": 20180507, "orderId": "20180507175020345000034567893", "processInstanceId": 302, "taskId": 326, "prodName": "有借有還", "taskName": "人工稽核", "userId": "68" }], "page": { "curPage": 1, "pageSize": 15 }, "query": { "assignerId": 55, "egtCreateTime": "2018-04-25 10:10:45", "isAssigned": 0 }, "code": 0, "message": "查詢成功" }
現在專案的目錄結構變成如下了:
### 目錄結構如下: demo1 # 工程名 | |--- dist # 打包後生成的目錄檔案 | |--- node_modules # 所有的依賴包 | |--- app | | |---index | | | |-- views # 存放所有vue頁面檔案 | | | | |-- parent.vue # 父元件 | | | | |-- child.vue # 子元件 | | | | |-- index.vue | | | |-- components # 存放vue公用的元件 | | | |-- js # 存放js檔案的 | | | |-- json # 存放所有json模擬資料的檔案 | | | | |-- parent_yyyy.json # json檔案 | | | |-- store # store倉庫 | | | | |--- actions.js | | | | |--- mutations.js | | | | |--- state.js | | | | |--- mutations-types.js | | | | |--- index.js | | | |-- app.js # vue入口配置檔案 | | | |-- router.js # 路由配置檔案 | |--- views | | |-- index.html # html檔案 | |--- webpack.config.js # webpack配置檔案 | |--- .gitignore | |--- README.md | |--- package.json | |--- .babelrc # babel轉碼檔案
那麼我這邊就不使用mock資料隨機生成了,直接把介面文件對應的資料格式貼到我對應json檔案內部,然後在頁面
模擬的時候,需要配置下;
1. 首先需要安裝 cross-env 外掛, 該外掛的具體用途請看 (https://www.cnblogs.com/tugenhua0707/p/9780621.html) , 安裝命令如下:
npm install --save cross-env
2. 在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" },
如上命名配置,當我們使用 npm run mock 的話,那麼就執行mock資料,當我們執行 npm run dev 的話,就是不mock資料,使用開發環境正式介面資料,當我們npm run build 的話,就是線上正式打包。
3. 在webpack中如何配置呢?
module.exports = { plugins: [ // 設定環境變數資訊 new webpack.DefinePlugin({ PRODUCTION: JSON.stringify(true), VERSION: JSON.stringify('5fa3b9'), BROWSER_SUPPORTS_HTML5: true, TWO: '1+1', 'typeof window': JSON.stringify('object'), 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) } }) ] }
4. 在vue頁面中如何使用呢?
<template> <div> </div> </template> <script type="text/javascript"> import { mapActions } from 'vuex'; export default { data() { return { } }, created() { this.testMock(); }, methods: { testMock() { console.log(process.env.NODE_ENV); if (process.env.NODE_ENV === 'mock') { const Mock = require('mockjs'); const json = require('../json/parent_yyyy.json'); Mock.mock('//xxx.abc.com/xxxx/yyy', 'get', { 'list': json }); } // 請求的地址是 '//xxx.abc.com/xxxx/yyy' Promise.all([this.commonActionGet(['getPower', {}])]).then((res) => { console.log(res); let rets; if (process.env.NODE_ENV === 'mock') { rets = res[0].list; } else { rets = res; } console.log(rets); }); }, ...mapActions(['commonActionGet', 'commonActionGetJSON', 'commonActionPost', 'commonActionPostJSON']) }, mounted() { } } </script>
列印如下圖所示:
可以看到設定環境為 mock了,並且模擬出了我們想要的資料,現在我們繼續 npm run dev 後,再來看看控制檯就會看到不會使用mock資料了,而直接訪問介面,如下圖所示:
如上看到控制檯輸出的是 developmemt 開發環境了。
同理,正式環境執行 npm run build 後打包,看到js檔案也不會包含 mock相關的程式碼了。這就可以在開發環境下mock資料了,並且不需要依賴開發的介面,直接根據文件的介面資料,就可以直接模擬介面返回的資料了。