3
我与我周旋久,宁作我

在前端开发中,使用 Mock 方案可以帮助开发人员在不依赖于后端接口的情况下进行开发和调试。Mock 数据可以模拟后端接口的返回结果,使得前端开发可以独立进行,不受后端接口的限制。以下是几种常见的前端Mock 方案:

手动编写Mock数据

手动编写 Mock 数据是一种简单而直接的前端Mock 方案。你可以手动创建 JSONJavaScript 对象来模拟后端接口的返回数据。下面是一个示例,展示如何手动编写 Mock 数据:

// 模拟用户列表接口的Mock数据
const mockUsers = [
  { id: 1, name: "John Doe", email: "john@example.com" },
  { id: 2, name: "Jane Smith", email: "jane@example.com" },
  { id: 3, name: "Bob Johnson", email: "bob@example.com" }
];

const sleep = (wait) => new Promise((resolve) => setTimeout(resolve, wait));

// 模拟获取用户列表的接口
axios.get("/api/users").then(() => sleep(500)).then(() => mockUsers);

这种方式适用于简单的场景,但对于复杂的接口或大量数据的情况下,手动编写 Mock 数据可能会变得繁琐。

Mock.js

Mock.js 是一个前端模拟数据生成器,可以帮助开发人员快速生成随机数据,并拦截 Ajax 请求。它提供了丰富的数据模板语法和模拟请求拦截功能,可以模拟后端接口的返回结果。Mock.js 可以轻松集成到前端项目中,并与其他前端框架(如VueReact等)配合使用。

在引入 Mock.js 库时,会执行初始化操作。这包括创建全局的 Mock 对象和生成 MockXMLHttpRequest 构造函数来完整地模拟原生 XHR(XMLHttpRequest)的行为。然后,当调用 Mock.mock() 方法时,Mock.js 开始解析数据模板,并生成模板数据,存储到缓存变量中。

为了能够拦截和处理前端发送的 AJAX 请求,Mock.js 将全局的 window.XMLHttpRequest 对象替换为 MockXMLHttpRequest。由于许多前端框架和库(如 axios)底层也使用了 XMLHttpRequest 发送网络请求,现在 XMLHttpRequest 被替换成了 MockXMLHttpRequest,从而使得 Mock.js 能够拦截这些请求。

当使用 axios 或其他基于 XMLHttpRequest 的网络请求库发送请求时,如果有匹配的数据模板,MockXMLHttpRequest 将会返回模拟数据,并将请求和响应状态同步给 axios(比如 axios 中监听了 readystatechange 事件,MockXMLHttpRequest 模拟响应后需要触发 readystatechange)。这样,前端开发人员可以在开发和调试过程中使用模拟数据,而无需实际访问后端接口。

如果没有匹配的数据模板,MockXMLHttpRequest 内部会调用原生 XMLHttpRequest 发送请求,并将请求和响应状态同步给 axios,以便实际的网络请求能够正常进行。

通过以上流程,Mock.js 可以方便地拦截和模拟前端的网络请求,提供模拟数据,以支持前端开发和调试的需求。

当在 React 中使用 Mock.js,你可以通过以下示例来模拟网络请求和使用模拟数据:

import React from 'react';
import Mock from 'mockjs';
import axios from 'axios';

class MyComponent extends React.Component {
  componentDidMount() {
    // 定义数据模板和拦截规则
    Mock.mock('/api/users', 'get', {
      'list|5': [{
        'id|+1': 1,
        'name': '@cname',
        'age|18-60': 1
      }]
    });

    // 拦截请求并延迟响应
    Mock.setup({
        timeout: '1000-3000'
    });

    // 使用模拟数据
    axios.get('/api/users')
      .then(response => {
        console.log(response.data); // 模拟数据
        // 处理模拟数据逻辑
      })
      .catch(error => {
        // 处理错误逻辑
      });
  }

  render() {
    // 组件渲染逻辑
    return (
      // JSX 界面代码
    );
  }
}

在上面的示例中,我们在 componentDidMount() 生命周期方法中定义了一个 GET 请求的数据模板和拦截规则,并使用模拟数据来处理网络请求。当组件挂载后,Mock.js 会拦截 /api/usersGET 请求并返回模拟数据。在使用 axios 发起网络请求后,可以通过 then 回调处理返回的模拟数据。

虽然 Mock.js 是一个强大的工具,但也存在一些局限性,包括:

  • 无法模拟复杂的后端逻辑:Mock.js 主要用于模拟接口返回的数据,但无法完全模拟后端的复杂逻辑和业务流程(增删改)。对于需要与后端进行深度交互的场景,Mock.js 可能无法提供准确的模拟数据和行为。
  • 需要手动编写数据模板和拦截规则:使用 Mock.js 需要手动编写数据模板和拦截规则,这对于复杂的数据结构和接口规范可能需要花费较多的时间和精力。对于大型项目或频繁变动的接口,维护这些规则可能会带来一定的工作量。
  • 对于使用非 XMLHttpRequest 的网络请求库支持有限:Mock.js 主要拦截和模拟 XMLHttpRequest 的请求,对于使用其他网络请求库(如 fetch)的项目,Mock.js 的支持可能相对有限。需要额外的配置和适配才能与这些库进行集成。

axios-mock-adapter

Axios 实例的 defaults.adapter 属性用于配置适配器,默认情况下,Axios 使用 xhrAdapter 作为适配器,基于浏览器的 XMLHttpRequest 对象发送请求。而在 Node.js 环境中,Axios 使用 httpAdapter 作为适配器,利用 Node.jshttp 模块发送请求。

image.png

axios-mock-adapter 是一个与 Axios 配合使用的插件。它通过实例化一个 mockAdapter 对象,并将其指定给 Axios 实例的 defaults.adapter 属性,从而将其作为适配器。

当使用 Axios 发送请求时,Axios 实例会根据 defaults.adapter 配置的适配器来处理请求。在正常情况下,请求会被发送到服务器,并返回服务器的响应。然而,当 defaults.adapter 被设置为 mockAdapter 时,axios-mock-adapter 将拦截匹配的请求,并返回预先定义的模拟数据,而不会实际发送请求到服务器。

function adapter() {
  return function (config) {
    var mockAdapter = this;
    return new Promise(function (resolve, reject) {
      handleRequest(mockAdapter, resolve, reject, config);
    });
  }.bind(this);
}

function MockAdapter(axiosInstance, options) {
  reset.call(this);

  if (axiosInstance) {
    this.axiosInstance = axiosInstance;
    // Clone the axios instance to remove interceptors
    // this is used for the passThrough mode with axios > 1.2
    this.axiosInstanceWithoutInterceptors = axiosInstance.create
      ? axiosInstance.create()
      : undefined;

    this.originalAdapter = axiosInstance.defaults.adapter;
    this.delayResponse =
      options && options.delayResponse > 0 ? options.delayResponse : null;
    this.onNoMatch = (options && options.onNoMatch) || null;
    // 指定适配器
    axiosInstance.defaults.adapter = this.adapter.call(this);
  } else {
    throw new Error("Please provide an instance of axios to mock");
  }
}

function handleRequest(mockAdapter, resolve, reject, config) {
  // code...

  var handler = utils.findHandler(
    mockAdapter.handlers,
    config.method,
    url,
    config.data,
    config.params,
    (config.headers && config.headers.constructor.name === 'AxiosHeaders')
      ? Object.assign({}, config.headers)
      : config.headers,
    config.baseURL
  );

  if (handler) {
    // 有匹配路由,返回模拟数据
  } else {
    // 没有匹配路由,走实际请求
  }
}

以下是在 React 中使用 axios-mock-adapter 示例:

import React, { useEffect } from 'react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

const YourComponent = () => {
  useEffect(() => {
    const mock = new MockAdapter(axios);
    // 在这里设置模拟请求的规则和响应
    mock.onGet('/api/endpoint').reply(200, { data: 'mocked data' });
    mock.onPost('/api/endpoint').reply(200, { data: 'mocked data' });

    // 使用 Axios 发送请求
    axios.get('/api/endpoint').then((response) => {
      console.log(response.data);
    });
  }, []);

  return <div>...</div>;
};

axios-mock-adapter 是一个用于模拟和测试 Axios 请求的强大工具,然而,使用 axios-mock-adapter 也有一些缺点,主要缺点就是 axios-mock-adapter 只能与 axios 搭配使用,不能支持复杂的业务场景(增删改)。

webpack-dev-mock

mock-dev-server 是一个基于 Express 的中间件,它允许你自定义中间件来拦截请求,并根据路由匹配返回模拟数据,否则将继续发送实际请求。

以下是 mock-dev-server 关键核心代码:

app.use(function (req, res, next) {
    // 请求拦截,匹配路由
    var match = mockConfig.length && (0, matchPath_1.default)(req, mockConfig);
    if (match) {
        debug("mock matched: [" + match.method + "] " + match.path);
        // 如果有匹配路由,则执行自定义中间件
        return match.handler(req, res, next);
    }
    else {
        // 如果没有匹配路由,则执行后续中间件行为,发送实际请求
        return next();
    }
});

在使用 React 进行开发时,你可以通过 webpack-dev-server 搭建本地开发服务器,并结合 mock-dev-server 中间件来实现请求拦截和模拟数据返回的功能。webpack-dev-server 提供了一个名为 before 的回调函数,会自动查找项目中是否存在 setupProxy.js 文件,并将其作为配置文件来添加中间件。

before(app, server) {
    // Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
    // middlewares before `redirectServedPath` otherwise will not have any effect
    // This lets us fetch source contents from webpack for the error overlay
    app.use(evalSourceMapMiddleware(server));
    // This lets us open files from the runtime error overlay.
    app.use(errorOverlayMiddleware());
    // 自动查找项目中是否存在 setupProxy.js 文件
    if (fs.existsSync(paths.proxySetup)) {
        // This registers user provided middleware for proxy reasons
        require(paths.proxySetup)(app);
    }
},

以下是 React 项目使用 mock-dev-server 模拟数据的示例:

(1)配置模拟数据

// mock/index.js
module.exports = {
    // 支持参数
    'POST /api/users/:id': (req, res) => {
        const { id } = req.params;
        res.send({ id: id });
    },
}

(2)在 setupProxy.js 添加自定义中间件

const proxy = require('http-proxy-middleware');
const mockDevServer = require('mock-dev-server');
module.exports = function (app) {
    mockDevServer(app); // 默认读取项目目录下的 mock/index.js 文件生成,会配置对应的接口。

    // 代理到开发环境
    app.use(proxy([
        "/api/xxx"
    ], {
      target: 'https://example.com/',
      changeOrigin: true,
    }));
};

mock-dev-server 是一个在前端开发中常用的工具,用于实现请求拦截和模拟数据返回的功能。它具有以下优点和缺点:

优点:

  • 灵活性:mock-dev-server 允许开发人员自定义中间件来拦截请求并返回模拟数据,因此具有很高的灵活性。你可以根据需要定义路由和对应的模拟数据,满足项目的特定需求。
  • 易于集成:mock-dev-server 基于 Express,与常见的前端开发工具和框架集成较为容易。你可以将其作为 webpack-dev-server 的中间件来使用,或者与其他构建工具或框架搭配使用,如 create-react-appVue CLI 等。

缺点:

  • 无法模拟复杂的业务场景:尽管 mock-dev-server 可以模拟后端接口的响应,但它无法模拟后端的业务逻辑。对于一些复杂的业务场景或后端计算,可能无法完全模拟真实的后端行为。

json-server

json-server 是一个基于 Node.js 的工具,可以根据提供的 JSON 文件快速搭建一个 RESTful API 的临时服务器。开发人员可以将需要模拟的数据存储在 JSON 文件中,然后使用 json-server 启动一个本地服务器,前端通过发送 HTTP 请求来获取数据。这个方案简单易用,适用于快速搭建临时的 Mock 服务器。

json-server 使用 Express 来搭建服务和管理路由。当你提供了路由配置文件时,json-server 将按照该文件中定义的规则来生成路由。这样你可以更灵活地定义资源的路由和操作方式。如果没有提供路由配置文件,json-server 会根据数据文件的结构自动生成默认的路由,包括对数据的增删改查操作。

此外,json-server 还支持自定义中间件。你可以添加自己的中间件函数来实现额外的功能,例如身份验证、日志记录等。这样你可以在 API 请求的不同阶段进行自定义处理。

另一个重要的特性是,当数据文件发生变更时,json-server 会自动重新启动服务。这意味着你可以轻松地修改和更新数据文件,而无需手动停止和重新启动服务器,从而提高开发效率。

以下是一个使用 json-server 的简单示例:

首先,安装所需的依赖:

$ yarn add json-server global

接下来,创建一个名为 db.json 的数据文件,作为 json-server 的数据源。在该文件中,可以定义一些示例数据,例如:

{
  "posts": [
    { "id": 1, "title": "Post 1", "content": "This is the content of Post 1" },
    { "id": 2, "title": "Post 2", "content": "This is the content of Post 2" }
  ]
}

然后,创建一个名为 api.js 的文件,以使用 axios 库来发送 HTTP 请求。示例代码如下:

import axios from 'axios';

const api = axios.create({
  baseURL: '/',
});

export const getPosts = () => api.get('/posts');
export const getPost = (id) => api.get(`/posts/${id}`);
export const createPost = (data) => api.post('/posts', data);
export const updatePost = (id, data) => api.put(`/posts/${id}`, data);
export const deletePost = (id) => api.delete(`/posts/${id}`);

const apis = {
  getPosts,
  getPost,
  createPost,
  updatePost,
  deletePost,
};

export default apis;

接下来,创建一个名为 Posts.js 的组件,用于展示帖子列表。该组件将使用 api.js 中定义的函数来获取帖子数据。示例代码如下:

import React, { useEffect, useState } from 'react';
import api from './api';

const Posts = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const response = await api.getPosts();
      setPosts(response.data);
    };

    fetchData();
  }, []);

  return (
    <div>
      <h2>Posts</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <h3>{post.title}</h3>
            <p>{post.content}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Posts;

现在,在终端中执行以下命令启动 json-server

$ json-server --watch db.json --port 3001

配置反向代理:

// setupProxy.js
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
  app.use(proxy([
    "/posts",
  ], {
    target: 'http://localhost:3001',
    changeOrigin: true,
  }));
};

然后,在另一个终端窗口中执行以下命令以启动 React 应用程序:

$ yarn start

Chrome Local Overrides

Chrome 117 版本开始,引入了一项新功能,它允许开发者覆盖XHR(XMLHttpRequest)fetch请求的内容。您可以在不依赖真实后端数据的情况下进行开发和调试。您可以自定义响应,模拟各种不同的情况,如成功响应、错误响应等,以确保您的网页在各种场景下都能正常运行。

Chrome 浏览器的 Network 面板中,您可以右键点击特定的网络请求,并选择"Override content"(覆盖响应)。这将自动在开发者工具的 Sources 面板的 Overrides 部分生成一个本地的响应文件。

image.png

可以在本地响应文件自定义的响应内容,包括状态码、响应头和响应体等,模拟不同业务场景的响应。

image.png

第三方Mock平台

使用第三方的 Mock 平台可以帮助你进行 API 的模拟和模拟数据的生成,下面以 Fast Mock 平台为例:

在平台配置了一个 Mock 接口 /user/address
image.png

然后配置代理:

// setupProxy.js
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
  // 匹配反向代理
  app.use(proxy([
    "/user/address",
  ], {
    target: 'https://www.fastmock.site/mock/6132bf44cf9f905257ca12ba9312ea10',
    changeOrigin: true,
  }));

  // 或者使用中间件匹配路由,307 重定向
  app.use((req, res, next) => {
    // 匹配路由
    if (/^\/user\/address$/.test(req.path)) {
      res.redirect(307, 'https://www.fastmock.site/mock/6132bf44cf9f905257ca12ba9312ea10/user/address');
    }
    next();
  });
};

image.png


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。