在react项目中让webpack使用mock数据

1. 创建react项目

使用 create-react-app 创建项目

npx create-react-app react-mock

执行 eject 命令

npm run eject

 删除 package.json 文件中的 eslintConfig 选项

2. 安装依赖包

npm i path-to-regexp fast-glob chokidar axios

3. 创建中间件

在 config 文件夹中创建 WebpackMiddlewareMock.js 文件

让webpack-dev-server加载中间件,把mock配置文件的请求地址和响应数据映射到dev server的路由上

const { pathToRegexp } = require("path-to-regexp");
const fg = require("fast-glob");
const path = require("path");
const chokidar = require("chokidar");

const VALID_METHODS = [
  "GET",
  "POST",
  "PUT",
  "DELETE",
  "PATCH",
  "HEAD",
  "OPTIONS",
];
const DEFAULT_METHOD = "GET";
const MOCK_FILE_PATTERN = "mock/**/*.{js,ts}";
const DEFAULT_OPTIONS = {
  rootDir: "",
  exclude: [],
};

class WebpackMiddlewareMock {
  constructor(options = {}) {
    this.options = { ...DEFAULT_OPTIONS, ...options };
    const { rootDir, exclude } = this.options;
    this.mockConfigs = this.getConfigs(rootDir, exclude);
  }

  parseMockKey(key) {
    const keyItems = key.split(/\s+/);
    if (keyItems.length === 1) {
      return {
        method: DEFAULT_METHOD,
        path: keyItems[0],
      };
    } else {
      const [method, path] = keyItems;
      const upperCaseMethod = method.toLocaleUpperCase();
      if (!VALID_METHODS.includes(upperCaseMethod)) {
        console.error(`method ${method} is not supported`);
      }
      if (!path) {
        console.error(`${key} path is not defined`);
      }
      return {
        method,
        path,
      };
    }
  }

  getConfigs(rootDir, exclude) {
    const ignore = exclude.map(
      (ele) => `mock${ele.startsWith("/") ? "" : "/"}${ele}`
    );
    const mockFiles = fg
      .sync(MOCK_FILE_PATTERN, {
        cwd: rootDir,
        ignore,
      })
      .map((file) => path.join(rootDir, file));

    const mockConfigs = [];
    mockFiles.forEach((mockFile) => {
      // disable require cache
      delete require.cache[mockFile];
      let mockModule;
      try {
        mockModule = require(mockFile);
      } catch (error) {
        console.error(`Failed to parse mock file ${mockFile}`);
        console.error(error);
        return;
      }
      const config = mockModule.default || mockModule || {};
      for (const key of Object.keys(config)) {
        const { method, path } = this.parseMockKey(key);
        const handler = config[key];
        if (
          !(
            typeof handler === "function" ||
            typeof handler === "object" ||
            typeof handler === "string"
          )
        ) {
          console.error(
            `mock value of ${key} should be function or object or string, but got ${typeof handler}`
          );
        }
        mockConfigs.push({
          method,
          path,
          handler,
        });
      }
    });

    return mockConfigs;
  }

  matchPath(req, mockConfigs) {
    for (const mockConfig of mockConfigs) {
      const keys = [];
      if (req.method.toLocaleUpperCase() === mockConfig.method) {
        const re = pathToRegexp(mockConfig.path, keys);
        const match = re.exec(req.path);
        if (re.exec(req.path)) {
          return {
            keys,
            match,
            mockConfig,
          };
        }
      }
    }
  }

  decodeParam(val) {
    if (typeof val !== "string" || val.length === 0) {
      return val;
    }
    try {
      return decodeURIComponent(val);
    } catch (error) {
      if (error instanceof URIError) {
        error.message = `Failed to decode param ' ${val} '`;
        error.status = 400;
        error.statusCode = 400;
      }
      throw error;
    }
  }

  createWatch() {
    const watchDir = this.options.rootDir;
    const watcher = chokidar
      .watch(watchDir, {
        ignoreInitial: true,
        ignored: [/node_modules/],
      })
      .on("all", () => {
        const { rootDir, exclude } = this.options;
        this.mockConfigs = this.getConfigs(rootDir, exclude);
      });
    return watcher;
  }

  createMiddleware() {
    const middleware = (req, res, next) => {
      const matchResult = this.matchPath(req, this.mockConfigs);
      if (matchResult) {
        const { match, mockConfig, keys } = matchResult;
        const { handler } = mockConfig;
        if (typeof handler === "function") {
          const params = {};
          for (let i = 1; i < match.length; i += 1) {
            const key = keys[i - 1];
            const prop = key.name;
            const val = this.decodeParam(match[i]);
            if (val !== undefined) {
              params[prop] = val;
            }
          }
          req.params = params;
          handler(req, res, next);
          return;
        } else {
          return res.status(200).json(handler);
        }
      } else {
        next();
      }
    };
    this.createWatch();
    return {
      name: "mock",
      middleware: middleware,
    };
  }

  static use(options) {
    const instance = new WebpackMiddlewareMock(options);
    const middleware = instance.createMiddleware();
    return middleware;
  }
}

module.exports = WebpackMiddlewareMock;

4. 修改webpackDevServer

修改 config/webpackDevServer.config.js 文件

引入 WebpackMiddlewareMock 中间件

const WebpackMiddlewareMock = require("./WebpackMiddlewareMock");

删除 onBeforeSetupMiddleware 和 onAfterSetupMiddleware 选项,替换 setupMiddlewares 选项

    setupMiddlewares: (middlewares, devServer) => {
      const mockMiddleware = WebpackMiddlewareMock.use({
        rootDir: paths.appPath,
      });
      middlewares.unshift(mockMiddleware);
      return middlewares;
    },

在项目根目录创建 mock 文件夹,并创建 user.js 文件

module.exports = {
  // 返回值是 String 类型
  "GET /api/name": "tom",
  // 返回值 Array 类型
  "POST /api/users": [
    { name: "foo", id: 0 },
    { name: "bar", id: 1 },
  ],
  "GET /api/users/:id": (req, res) => {
    res.send({
      params: req.params,
    });
  },
  // 返回值是 Object 类型
  "DELETE /api/users/1": { name: "bar", id: 1 },
};

5. 测试mock请求

修改 App.js 文件

import { useState } from "react";
import axios from "axios";

function App() {
  const [resultGet, setResultGet] = useState("");
  const [resultPost, setResultPost] = useState("");
  const [resultParams, setResultParams] = useState("");
  const [resultDelete, setResultDelete] = useState("");

  const handleGet = async () => {
    const res = await axios.get("/api/name");
    setResultGet(res.data);
  };
  const handlePost = async () => {
    const res = await axios.post("/api/users");
    setResultPost(JSON.stringify(res.data));
  };
  const handleParams = async () => {
    const res = await axios.get("/api/users/100");
    setResultParams(JSON.stringify(res.data));
  };
  const handleDelete = async () => {
    const res = await axios.delete("/api/users/1");
    setResultDelete(JSON.stringify(res.data));
  };

  return (
    <div className="App">
      <button onClick={handleGet}>"GET /api/name"</button>
      <h2>{resultGet}</h2>
      <hr />

      <button onClick={handlePost}>"POST /api/users"</button>
      <h2>{resultPost}</h2>
      <hr />

      <button onClick={handleParams}>"GET /api/users/:id"</button>
      <h2>{resultParams}</h2>
      <hr />

      <button onClick={handleDelete}>"DELETE /api/users/1"</button>
      <h2>{resultDelete}</h2>
      <hr />
    </div>
  );
}

export default App;

启动项目测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/726616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android【SDK】 SDK是如何开发的,怎么打包aar包

文章目录 一、Android SDK开发示例工程二、Android SDK的开发三、打包aar包四、Android SDK的使用 一、Android SDK开发示例工程 本教程工程Git链接&#xff1a;https://gitcode.com/xiaohuihui1400/AndroidSdkExample/overview 二、Android SDK的开发 新建项目&#xff0c;…

Linux驱动开发笔记(九)IIC子系统及其驱动

文章目录 前言一、IIC驱动框架二、总线驱动2.1 iic总线的运行机制2.2 重要数据结构2.2.1 i2c_driver结构体2.2.2 i2c总线结构体 2.3 匹配规则 三、设备树的修改四、设备驱动的编写4.1 相关API函数4.1.1 i2c_add_adapter( )4.1.2 i2c_register_driver( )4.1.3 i2c_transfer( )4.…

MDK-ARM 编译后 MAP 文件分析

本文配合 STM32 堆栈空间分布 食用更佳&#xff01; 一图胜千言。。。

【LLM之NL2SQL】DAIL-SQL论文阅读笔记

研究背景 该研究旨在提供一个全面、系统的评估框架&#xff0c;用于评估基于大型语言模型&#xff08;LLM&#xff09;的Text-to-SQL技术。特别强调了不同的提示工程策略的有效性和效率&#xff0c;以及开源LLM的可行性。研究的重点是评估在零样本和少样本场景下的不同问题表示…

ICC2如何写DCG需要的floorplan信息

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 DCG需要哪些floorplan信息呢? 1)fixed属性的port和mem / ip / io 2)boundary信息 3)电源域形状 前两条都可以写到def里,电源域需要用脚本处理,这里分享一下脚本。 set_fixed_objects [ge…

# 消息中间件 RocketMQ 高级功能和源码分析(七)

消息中间件 RocketMQ 高级功能和源码分析&#xff08;七&#xff09; 一、 消息中间件 RocketMQ 源码分析&#xff1a;消息存储核心类介绍 1、消息存储在 store 模块中。消息存储核心类 DefaultMessageStore.java 2、消息存储核心类介绍 private final MessageStoreConfig me…

react18 实现具名插槽

效果预览 技术要点 当父组件给子组件传递的 JSX 超过一个标签时&#xff0c;子组件接收到的 children 是一个数组&#xff0c;通过解析数组中各 JSX 的属性 slot &#xff0c;即可实现具名插槽的分发&#xff01; 代码实现 Father.jsx import Child from "./Child";…

DNF安卓分离仅是开始:游戏厂商积极布局自有渠道,市场变革在即

毫无征兆&#xff0c;DNF手游今天突然宣布从各大安卓平台下架。 《地下城与勇士:起源》运营团队于6月19日发布声明&#xff0c;指出因合约到期&#xff0c;游戏将不再上架部分安卓平台的应用商店。然而&#xff0c;这一事件并非完全无迹可循。 早在2021年初&#xff0c;华为游…

ROM以及ROM与RAM对比

1.ROM ROM最原始的定义是“只读存储器”&#xff0c;一旦写入原始信息则不能更改。所以ROM通常用来存放固定不变的程序、常数和汉字字库&#xff0c;甚至用于操作系统的固化。它与随机存储器可共同作为主存的一部分&#xff0c;统一构成主存的地址域。 现在已经发展出了很多R…

商超便利店收银系统源码推荐

细节决定成败&#xff0c;无论是做什么事情都要注重细节&#xff0c;让我们来看看关于商超便利店陈列的“细节”有哪些需要注意的地方。 首先要注意商品不要摆太高&#xff0c;放在适当位置即可&#xff01; 商超便利店内&#xff0c;销量最佳的物品摆放位置依次为与顾客视线…

ICC2如何写出floorplan信息

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 1)write_def 不用-exclude 的话rows_tracks / vias /blockages / routing_rules信息都会写出去,即便用了-include {macro ports}也不行,所以不介意这些东西的话可以用以下两种种def方式去写f…

RabbitMQ的简单使用 —— Python篇

&#xff08;一&#xff09;RabbitMQ的简介 RabbitMq 是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理中间件。消息队列是一种应用程序对应用程序的通行方式&#xff0c;应用程序通过写消息&#xff0c;将消息传递于队列&#xff0c;由另一应用程序读取 完…

nacos 配置修改.代码实时刷新

再类上用 RefreshScope 更新Value(“${uniqlo.privacy.url:https://wsurl.cc/yourls-api.php}”) private String shortLinkGenerateUrl;的数据可以实时更新 2.再配置类上次用 ConfigurationProperties(prefix “test.privacy”) 和nacos的配置一直 Data Component Configu…

redis高可用-哨兵机制

一&#xff1a;背景 上一节我们已经实现了redis的主从同步&#xff0c;从而实现服务的流量分摊和数据高可用&#xff0c;但是出现故障以后&#xff0c;需要人工手动接入&#xff0c;手动切换主从&#xff0c;来实现故障转移。这是比较麻烦的&#xff0c;毕竟人不能实时盯着服务…

深度学习算法informer(时序预测)(四)(Decoder)

一、DecoderLayer架构如图&#xff08;不改变输入形状&#xff09; 二、Decoder整体 包括两部分 1. 多层DecoderLayer 2. 层归一化 代码如下 class DecoderLayer(nn.Module):def __init__(self, self_attention, cross_attention, d_model, d_ffNone,dropout0.1, activati…

java溯本求源之基础(二十五)之--ArrayList常用方法介绍

1. 介绍 1.1简介 ArrayList ArrayList 是 Java 集合框架中的一个类&#xff0c;位于 java.util 包中。它实现了 List 接口&#xff0c;提供了一个动态数组的功能。与普通数组不同&#xff0c;ArrayList 可以在需要时自动调整其容量&#xff0c;以容纳更多的元素。这使得它非常…

《模拟联合国2.9—团队协作》

感谢上海财经大学持续的邀请&#xff0c;今天在阶梯教室举办的《模拟联合国2.0—团队协作》沙盘课程圆满结束。尽管场地的限制带来了一定的挑战&#xff0c;但得益于系统思考中“结构影响行为”的原则&#xff0c;我得以在不同场景中巧妙设计课程结构&#xff0c;极大地促进了大…

【SEMI-e ·国际半导体深圳展】| 06月26-28日唯创知音语音芯片供应商 邀您来观展

世界聚焦半导体&#xff0c;产业规模空前&#xff01;一场高端产业研学盛会即将如约而至。 SEMI-e 第六届2024国际半导体展深圳站&#xff0c;2024年06月26-28日将在深圳国际会展中心&#xff08;宝安&#xff09;开展&#xff0c;展会展出面积60000平方米&#xff0c;汇聚全国…

Python | Leetcode Python题解之第155题最小栈

题目&#xff1a; 题解&#xff1a; class MinStack:def __init__(self):self.stack []self.min_stack [math.inf]def push(self, x: int) -> None:self.stack.append(x)self.min_stack.append(min(x, self.min_stack[-1]))def pop(self) -> None:self.stack.pop()sel…

Java+ffmpeg 合并两个mp4文件

使用ffmpeg测试命令 ffmpeg -i "E:\Monitor\video_20240617_10.mp4" -i "E:\Monitor\video1_20240617_10.mp4" -filter_complex "[0:v][0:a][1:v][1:a]concatn2:v1:a1[v][a]" -map "[v]" -map "[a]" -c:v libx264 -c:a…