Mock
在实际项目开发中,后端接口、接口文档可能晚于前端才会开发完成,前端只能等到后端的接口出来以后才能进行开发,这样一来对于前端就显得十分被动,如果有什么东西可以制造一些假的数据用于暂时的测试,模拟后台接口的话,对于前端的开发就方便许多了。而 Mock.js 就正好满足了这样的需求。它的特点就是生成 随机数据、拦截 Ajax 请求。
1. 安装
留着偷懒直接复制粘贴:
<script src="https://cdn.bootcdn.net/ajax/libs/Mock.js/1.0.1-beta3/mock-min.js"></script>
2. 生成随机数据:
语法:
Mock.mock(template); // template 一个配置对象,数据模版,可以是对象或字符串,根据数据模板来生成数据
参数 template 中的每个属性由 3 部分构成:属性名 name、生成规则 rule、属性值 value,属性名和生成规则间用 | 分隔,生成规则是可选的
"name|rule": value
语法:
Mock.mock({ key: value});
以下代码表示随机生成一个包含3个对象的数组,key 是 list ,value 是一个 array,array 中的每一个 item 又都是一个对象
let data = Mock.mock({ "list|3": [{ 'id|+1': 1 // 对象的 id 属性从 1 开始自增 }] }); console.log(data); // 如果需要 JSON 格式 则转换为 JSON.stringify(data);
输出:(忽略截图里 key 是 testList 而示例代码 key 是 list。任性,花时间解释,但就是不改!)
mock 常用值:
let data = Mock.mock({ // key 为 studentsArr, value 为一个数组。数组中随机生成 10 个对象 "studentsArr|10": [{ // id 属性依次 +1 "id|+1": 1, // 随机产生一个中文名 英文名为@name() "name": "@cname()", // 在 16 - 22 间随机产生一个数作为年龄 "age|16-22": 1, // 在数组中随机生成一个值作为性别 "gender|1": ["male", "female"], // 随机产生一个数字作为成绩 "score|40-100":1, // 随机产生一个生日 "birthday": "@date()", // 随机生成一个 email 地址 "email":"@email()", // 随机生成一个地址,参数可添 true "address": "@county()", // 随机生成一个手机号码,支持正则 "phoneNumber": /^1[3458]\d{9}$/ }] });
3. 拦截 Ajax 请求
Mock 作为客户端向服务器端请求路途中的拦路虎,会拦截 Ajax 请求,当拦截到匹配 rurl 的 Ajax 请求时,将根据数据模板 template 生成模拟数据并作为响应数据返回。
// rurl 表示要拦截的 url Mock.mock(rurl, template);
一个超简单的 demo:
<input type="button" value="获取数据"> <script src="https://cdn.bootcdn.net/ajax/libs/Mock.js/1.0.1-beta3/mock-min.js"></script> <script> const btn = document.querySelector('[type="button"]'); // 获取 button Mock.mock('./getData', function () { return Mock.mock({ 'name': '@cname' }); }); btn.onclick = function () { let xhr = new XMLHttpRequest(); xhr.open('GET', './getData'); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let data = xhr.responseText; console.log(data); } } }
按钮点击后,会发送 Ajax 请求,请求 ./getData 文件,但在请求过程中会被 Mock 给拦截,并返回出一个随机数据(第8、9行)一个拥有 “name” 的对象作为 xhr.responseText 的值。
Mock.mock(/getStudents/, function () { // rurl 可以使用正则也可以直接使用 Ajax open 的 url 地址 return Mock.mock({ "list|5": [{ "id|+1": 1, 'name': "@cname", "age|18-25": 1, "address": "@county(true)", "hobby|1": ["吃饭", "睡觉", "干仗"], "tel": /^1[3458]\d{9}$/ }], }); }); btn.addEventListener("click", function () { let xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP"); xhr.open("GET", "/getStudents"); xhr.send(null); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { let result = JSON.parse(xhr.responseText); console.log(result); } } });
4. Mock 搭配 Ajax 模拟登录
假设存在一个表单:
<form action="#" method="get"> <div> 账号 <input type="text" id='username' name='username'> </div> <div> 密码 <input type="text" id='password' name='password'> </div> <input type="button" value="提交"> </form>
输入内容点击提交后,会在地址栏发现:test.html?username=123&password=123# 即 “=” 左侧是 input 框 name 属性,右侧是实际输入的值,被拼接在 .html 路径后
以下所有代码文件名都有出现不对应的现象。故意的。钓鱼执法,懂?
现在可以携带这些信息,发送 Ajax 请求,再被 Mock 拦截模拟服务器处理,再返回结果:
// 点击按钮发送 Ajax 请求 btn.onclick = function () { let xhr = new XMLHttpRequest(); xhr.open('GET', `./test.html?username=${txt.value}&password=${pwd.value}`); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let data = xhr.responseText; } } }
Mock 部分:
/* mock() 参数: 第 1 个参数格式还可以写 RegExp,表示拦截含有 XX 字符的路径 第 2 个参数除了可以写返回的随机数据,还可以是一个 callback 进行处理,再将结果返回给前台 */ Mock.mock(/test.html/, (options) => { // ... });
/* GET 请求的参数被放在 options 的 url 属性上 POST 请求的参数在 options 的 body 属性上 */ Mock.mock(/test.html/, (options) => { console.log(options); // {url: "./test.html?name=zhangsan&password=123", type: "GET", body: null} console.log(options.url); // ./test.html?name=zhangsan&password=123 });
既然都得到 ./test.html?name=zhangsan&password=123 了,取出账号 "zhangsan" 和密码 "123" 就很简单了
Mock.mock(/test.html/, (options) => { // 拆分 options.url 的值: ./test.html?name=zhangsan&password=123 let userContent = options.url.split('?')[1].split('&'); // ['name=zhangsan', 'password=123'] let username = userContent[0].split('=')[1], // zhangsan password = userContent[1].split('=')[1]; // 123 // 如果是本地存储,就以本地存储的形式去获取、判断,这里只做简单的变量判断 let result = users.some(item => item.username == username && item.password == password); return result; });
整合
// 用户信息如果存在 localStorage 就使用 localStorage 的存取 const users = [ { name: 'zhangsan', password: '123' }, { name: 'lisi', password: '456' } ]; Mock.mock(/test.html/, function (options) { let userContent = options.url.split('?')[1].split('&'); let username = userContent[0].split('=')[1], password = userContent[1].split('=')[1]; let result = users.some(item => item.username == username && item.password == password); // 模拟后台操作,返回结果给前台 return result; }); btn.onclick = function () { let xhr = new XMLHttpRequest(); xhr.open('GET', `./test.html?username=${txt.value}&password=${pwd.value}`); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let data = JSON.parse(xhr.responseText); // 回来的数据为 string if(data){ console.log('登录成功'); }else{ console.log('登录失败'); } } } }
如果是 POST 请求,发送的数据去观察 options.body ,不贴答案了。