在实际项目开发中,后端接口、接口文档可能晚于前端才会开发完成,前端只能等到后端的接口出来以后才能进行开发,这样一来对于前端就显得十分被动,如果有什么东西可以制造一些假的数据用于暂时的测试,模拟后台接口的话,对于前端的开发就方便许多了。而 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 ,不贴答案了。