前端 mock 数据的几种方式

具体需求开发前,后端往往只提供接口文档,对于前端,最简单的方式就是把想要的数据写死在代码里进行开发,但这样的坏处就是和后端联调前还需要再把写死的数据从代码里删除,最好的方式是无侵入的 mock 。下边介绍几种常用的方式,大家可以结合自己的项目来选取。

大致分为三类,重写 xhr/fetchnode.js 服务中转、系统层面拦截。

接口demo

为了后边方便的安装 node 包,可以用 webpack 进行打包,具体配置可以参考 2021年从零开发前端项目指南,看到 React 配置的前一步就够了,只需要配置一个 html 和一个接口请求。 需要注意下 webpack 的版本,不同版本后续的配置会不同,这里我用的是 5.75.0

最终目标是通过 mock 让下边还没有开发好的接口正常返回数据:

1
2
3
4
5
6
fetch("/api/data", {
body: JSON.stringify({ id: 10 }),
method: "POST",
})
.then((response) => response.json())
.then((json) => console.log(json));

现在肯定是 404

image-20221223093023287

Better-mock

better-mock forkMock.js,使用方法和 Mock.js 一致,用于 javascript mock 数据生成,它可以拦截 XHRfetch 请求,并返回自定义的数据类型。

只需要在调用接口前,引入 better-mock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Mock from "better-mock";

// mock list 返回数组,大小是 1 到 10,对象中的 id 自动加 1
Mock.mock("/api/data", {
"list|1-10": [
{
"id|+1": 1,
},
],
});

fetch("/api/data", {
body: JSON.stringify({ id: 10 }),
method: "POST",
})
.then((response) => response.json())
.then((json) => console.log(json));

控制台此时就会输出数据了。

image-20221224203508017

better-mock 一个好处就是可以通过它既有的语法来生成一些随机的数据,每次请求都会返回不同的数据。

坏处是会在请求发送前就拦截,导致在 Chrome 控制台就看不见请求了。

image-20221224203741121

just mock

just mock 是一个浏览器插件,在代码中什么都不需要更改,只需要添加相应的接口和数据即可实现拦截。

插件安装好后添加相应的域名就可以拦截到相应的请求。

image-20221224212934829

接着进行相应的编辑添加对应的 mock 数据就好。

image-20221224221427974

这样接口就会被拦截,控制台输出预设的数据:

image-20221224223523797

浏览器插件原理和 Better-mock 是一样的,但会更加轻便,无需融入到代码中。两者的原理是一样的,都是在网络请求前重写了全局的 xhrfetch ,具体可以参考 油猴脚本重写fetch和xhr请求

koa

本地通过 koa 开启一个接口服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// serve.js
const Koa = require("koa");
const router = require("koa-router")();
const app = new Koa();

router.post("/api/data", async (ctx, next) => {
ctx.response.body = {
status: true,
data: [1, 2, 3],
msg: "获取数据成功",
};
});
// add router middleware:
app.use(router.routes());

app.listen(3000);

本地开启运行:node server.js,接口提供的地址是 localhost:3000,但是请求的地址是 loacalhost:8080 ,当然可以直接修改代码里的地址为 localhost:3000 ,但还可以通过 webpack 的配置,将请求转发到 localhost:3000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
devServer: {
static: path.resolve(__dirname, "./dist"),
proxy: {
// 将 /api 开头的 http 请求,都代理到 localhost:3000 上,由 koa 提供 mock 数据
"/api": {
target: "http://localhost:3000",
secure: false,
},
},
},
};

这样就可以看到控制台输出了:

image-20221225101033687

此外,ChromeNetwork 也可以正常看到这个请求:

image-20221225101326328

这种方法也可以用来解决跨域问题,举个例子:

如果本地想访问一个具体域名的接口,比如请求知乎的热榜接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fetch(
"https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=50&desktop=true",
{
headers: {
accept: "*/*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-ab-param": "",
"x-ab-pb":
"CpoBCAAbAD8ARwC0AGkBagF0ATsCzALXAtgCoAOhA6IDtwOmBNYEEQVRBYsFjAWeBTAGMQbrBicHdAh2CHkI2gg/CWAJwwnECcUJxgnHCcgJyQnKCcsJzAnRCfQJBApJCmUKawqYCqUKqQq+CsQK1ArdCu0K/go7CzwLQwtGC3ELhwuNC9cL4AvlC+YLLAw4DHEMjwysDMMMyQz4DBJNAQAAAAAAAAAAAAAAAAAAAAAEAAEAAAEAAAEAAAIGAAABAAAAAAAAAAAAAAADAAAAAAEAAAABAQAAAAEAAQAAAAUCAQAABgIEAAACAAA=",
"x-api-version": "3.0.76",
"x-requested-with": "fetch",
"x-zse-93": "101_3_3.0",
"x-zse-96":
"2.0_LYJSVCX+9b1YXp/sG1Azyi5tC5RpabLbkXb3w5s6rv=Gxy9uMXqMXm4LjYWRdoIz",
"x-zst-81":
"3_2.0aR_sn77yn6O92wOB8hPZnQr0EMYxc4f18wNBUgpTQ6nxERFZfRY0-4Lm-h3_tufIwJS8gcxTgJS_AuPZNcXCTwxI78YxEM20s4PGDwN8gGcYAupMWufIoLVqr4gxrRPOI0cY7HL8qun9g93mFukyigcmebS_FwOYPRP0E4rZUrN9DDom3hnynAUMnAVPF_PhaueTF4C8IhwVIDO_8ioC0JXfW9CKpCwCs4OBQAc0uBefagCKGMo1yroBh9CKe_STVHC1IqLKHJL_chSflqHCOqgYPhYKVwH8M4Lqqq9y1wH967NC7vH80UC8wCHswgHBDgY_ovg9r0wBcJO8s9OCzcLMNgLfkgNByqCLhhUf_veOQRY_dvxmCg_zugS8iBtBFgOZkwNLDw2skTX18XSYuJLqpCYBo_pMWbS8Pv3YtGFBaqL9AwCYhbL9eGVV2rNClDL1wJLmxCgKagNBUwSqYrHBbGp8e8HGggSMQ7xC3rOs",
},
referrer: "https://www.zhihu.com/hot",
referrerPolicy: "no-referrer-when-downgrade",
body: null,
method: "GET",
mode: "cors",
credentials: "include",
}
);

由于本地域名是 http://localhost:8080/ ,此时浏览器就会报跨域的错了。

image-20221225102517407

此时后端可以通过 CORS 策略解决跨域的问题,但因为是测试环境,后端可能会说你自己解决吧,此时就可以通过 Koa 进行中转。

改写一下 Koa 的代码,先请求后端的接口,接着将收到的数据拿到后返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import Koa from "koa";
import fetch from "node-fetch";
import Router from "koa-router";

const app = new Koa();
const router = new Router();
router.post("/api/data", async (ctx, next) => {
const res = await fetch(
"https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=50&desktop=true",
{
headers: {
accept: "*/*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"sec-ch-ua":
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-ab-param": "",
"x-ab-pb":
"CpoBCAAbAD8ARwC0AGkBagF0ATsCzALXAtgCoAOhA6IDtwOmBNYEEQVRBYsFjAWeBTAGMQbrBicHdAh2CHkI2gg/CWAJwwnECcUJxgnHCcgJyQnKCcsJzAnRCfQJBApJCmUKawqYCqUKqQq+CsQK1ArdCu0K/go7CzwLQwtGC3ELhwuNC9cL4AvlC+YLLAw4DHEMjwysDMMMyQz4DBJNAQAAAAAAAAAAAAAAAAAAAAAEAAEAAAEAAAEAAAIGAAABAAAAAAAAAAAAAAADAAAAAAEAAAABAQAAAAEAAQAAAAUCAQAABgIEAAACAAA=",
"x-api-version": "3.0.76",
"x-requested-with": "fetch",
"x-zse-93": "101_3_3.0",
"x-zse-96":
"2.0_LYJSVCX+9b1YXp/sG1Azyi5tC5RpabLbkXb3w5s6rv=Gxy9uMXqMXm4LjYWRdoIz",
"x-zst-81":
"3_2.0aR_sn77yn6O92wOB8hPZnQr0EMYxc4f18wNBUgpTQ6nxERFZfRY0-4Lm-h3_tufIwJS8gcxTgJS_AuPZNcXCTwxI78YxEM20s4PGDwN8gGcYAupMWufIoLVqr4gxrRPOI0cY7HL8qun9g93mFukyigcmebS_FwOYPRP0E4rZUrN9DDom3hnynAUMnAVPF_PhaueTF4C8IhwVIDO_8ioC0JXfW9CKpCwCs4OBQAc0uBefagCKGMo1yroBh9CKe_STVHC1IqLKHJL_chSflqHCOqgYPhYKVwH8M4Lqqq9y1wH967NC7vH80UC8wCHswgHBDgY_ovg9r0wBcJO8s9OCzcLMNgLfkgNByqCLhhUf_veOQRY_dvxmCg_zugS8iBtBFgOZkwNLDw2skTX18XSYuJLqpCYBo_pMWbS8Pv3YtGFBaqL9AwCYhbL9eGVV2rNClDL1wJLmxCgKagNBUwSqYrHBbGp8e8HGggSMQ7xC3rOs",
},
referrer: "https://www.zhihu.com/hot",
referrerPolicy: "no-referrer-when-downgrade",
body: null,
method: "GET",
mode: "cors",
credentials: "include",
}
);
const data = await res.json();
ctx.response.body = {
status: true,
data,
msg: "获取数据成功",
};
});
app.use(router.routes());

app.listen(3000);

此时还是请求 /api/data

1
2
3
4
5
6
fetch("/api/data", {
body: JSON.stringify({ id: 10 }),
method: "POST",
})
.then((response) => response.json())
.then((json) => console.log(json));

依旧让 Webpack 将数据转发到 Koa

1
2
3
4
5
6
7
8
9
10
devServer: {
static: path.resolve(__dirname, "./dist"),
proxy: {
// 将 `/api` 开头的 http 请求,都代理到 `localhost:3000` 上,由 koa 提供 mock 数据
"/api": {
target: "http://localhost:3000",
secure: false,
},
},
},

现在控制台输出的就是知乎返回的数据了,跨域问题也消失了:

image-20221225104513837

当然上边解决跨域只是一个思路,具体的封装还需要结合项目来进行。

webpack

上边可以通过 webpack 进行转发数据,是因为 webpack 也启动了一个 HTTP 服务器,只不过用的不是 Koa ,是更早的一个框架 Express ,而且它们是同一个团队开发的。

既然已经有了一个 HTTP 服务器,所以也没必要再开启另一个 Koa 的了,通过给 webpack 传递一个函数,重写 Koa 返回的数据即可。

只需要通过 setupMiddlewares 重写数据即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
devServer: {
static: path.resolve(__dirname, "./dist"),
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error("webpack-dev-server is not defined");
}

middlewares.unshift({
path: "/api/data",
middleware: (req, res) => {
// mock 数据模拟接口数据
res.send({ list: [1, 2, 3], msg: "webpack mock" });
},
});

return middlewares;
},
},
};

此时控制台也可以看到输出的内容:

image-20221225111041070

同时 Network 也是可以看到网络请求的。

image-20221225111123332

Charles

终极必杀 mock 方法,因为它除了可以拦截浏览器中的请求,也可以拦截任意 App 的数据,甚至还可以拦截手机中的 HTTPS 请求,前段时间很火的羊了个羊就可以通过 Charles 抓取请求然后迅速通关。

需要注意的是 Charles 抓不到 localhost 的请求,访问的时候需要将 localhost 改为 localhost.charlesproxy.com

image-20221225122855681

webpack 需要加一个 allowedHosts 的配置,不然会返回 Invalid Host header

1
2
3
4
devServer: {
static: path.resolve(__dirname, "./dist"),
allowedHosts: "all",
},

全部配置好后就可以看到 Charles 抓到的请求了。

image-20221225115539515

此时只需要提前写好一个 json 文件,然后将右键选择 Map Local 对应的文件即可。

image-20221225115824423

1
2
3
4
{
"data": [1, 2, 3],
"msg": "from charles"
}

接下来就可以在控制台看到 mock 成功了。

image-20221225120440472

几种 mock 方式各有优缺点,上边只是提供一个思路,具体的 mock 方案就需要结合项目进行一定的封装了。

windliang wechat