• 在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的路由上

    1. const { pathToRegexp } = require("path-to-regexp");
    2. const fg = require("fast-glob");
    3. const path = require("path");
    4. const chokidar = require("chokidar");
    5. const VALID_METHODS = [
    6. "GET",
    7. "POST",
    8. "PUT",
    9. "DELETE",
    10. "PATCH",
    11. "HEAD",
    12. "OPTIONS",
    13. ];
    14. const DEFAULT_METHOD = "GET";
    15. const MOCK_FILE_PATTERN = "mock/**/*.{js,ts}";
    16. const DEFAULT_OPTIONS = {
    17. rootDir: "",
    18. exclude: [],
    19. };
    20. class WebpackMiddlewareMock {
    21. constructor(options = {}) {
    22. this.options = { ...DEFAULT_OPTIONS, ...options };
    23. const { rootDir, exclude } = this.options;
    24. this.mockConfigs = this.getConfigs(rootDir, exclude);
    25. }
    26. parseMockKey(key) {
    27. const keyItems = key.split(/\s+/);
    28. if (keyItems.length === 1) {
    29. return {
    30. method: DEFAULT_METHOD,
    31. path: keyItems[0],
    32. };
    33. } else {
    34. const [method, path] = keyItems;
    35. const upperCaseMethod = method.toLocaleUpperCase();
    36. if (!VALID_METHODS.includes(upperCaseMethod)) {
    37. console.error(`method ${method} is not supported`);
    38. }
    39. if (!path) {
    40. console.error(`${key} path is not defined`);
    41. }
    42. return {
    43. method,
    44. path,
    45. };
    46. }
    47. }
    48. getConfigs(rootDir, exclude) {
    49. const ignore = exclude.map(
    50. (ele) => `mock${ele.startsWith("/") ? "" : "/"}${ele}`
    51. );
    52. const mockFiles = fg
    53. .sync(MOCK_FILE_PATTERN, {
    54. cwd: rootDir,
    55. ignore,
    56. })
    57. .map((file) => path.join(rootDir, file));
    58. const mockConfigs = [];
    59. mockFiles.forEach((mockFile) => {
    60. // disable require cache
    61. delete require.cache[mockFile];
    62. let mockModule;
    63. try {
    64. mockModule = require(mockFile);
    65. } catch (error) {
    66. console.error(`Failed to parse mock file ${mockFile}`);
    67. console.error(error);
    68. return;
    69. }
    70. const config = mockModule.default || mockModule || {};
    71. for (const key of Object.keys(config)) {
    72. const { method, path } = this.parseMockKey(key);
    73. const handler = config[key];
    74. if (
    75. !(
    76. typeof handler === "function" ||
    77. typeof handler === "object" ||
    78. typeof handler === "string"
    79. )
    80. ) {
    81. console.error(
    82. `mock value of ${key} should be function or object or string, but got ${typeof handler}`
    83. );
    84. }
    85. mockConfigs.push({
    86. method,
    87. path,
    88. handler,
    89. });
    90. }
    91. });
    92. return mockConfigs;
    93. }
    94. matchPath(req, mockConfigs) {
    95. for (const mockConfig of mockConfigs) {
    96. const keys = [];
    97. if (req.method.toLocaleUpperCase() === mockConfig.method) {
    98. const re = pathToRegexp(mockConfig.path, keys);
    99. const match = re.exec(req.path);
    100. if (re.exec(req.path)) {
    101. return {
    102. keys,
    103. match,
    104. mockConfig,
    105. };
    106. }
    107. }
    108. }
    109. }
    110. decodeParam(val) {
    111. if (typeof val !== "string" || val.length === 0) {
    112. return val;
    113. }
    114. try {
    115. return decodeURIComponent(val);
    116. } catch (error) {
    117. if (error instanceof URIError) {
    118. error.message = `Failed to decode param ' ${val} '`;
    119. error.status = 400;
    120. error.statusCode = 400;
    121. }
    122. throw error;
    123. }
    124. }
    125. createWatch() {
    126. const watchDir = this.options.rootDir;
    127. const watcher = chokidar
    128. .watch(watchDir, {
    129. ignoreInitial: true,
    130. ignored: [/node_modules/],
    131. })
    132. .on("all", () => {
    133. const { rootDir, exclude } = this.options;
    134. this.mockConfigs = this.getConfigs(rootDir, exclude);
    135. });
    136. return watcher;
    137. }
    138. createMiddleware() {
    139. const middleware = (req, res, next) => {
    140. const matchResult = this.matchPath(req, this.mockConfigs);
    141. if (matchResult) {
    142. const { match, mockConfig, keys } = matchResult;
    143. const { handler } = mockConfig;
    144. if (typeof handler === "function") {
    145. const params = {};
    146. for (let i = 1; i < match.length; i += 1) {
    147. const key = keys[i - 1];
    148. const prop = key.name;
    149. const val = this.decodeParam(match[i]);
    150. if (val !== undefined) {
    151. params[prop] = val;
    152. }
    153. }
    154. req.params = params;
    155. handler(req, res, next);
    156. return;
    157. } else {
    158. return res.status(200).json(handler);
    159. }
    160. } else {
    161. next();
    162. }
    163. };
    164. this.createWatch();
    165. return {
    166. name: "mock",
    167. middleware: middleware,
    168. };
    169. }
    170. static use(options) {
    171. const instance = new WebpackMiddlewareMock(options);
    172. const middleware = instance.createMiddleware();
    173. return middleware;
    174. }
    175. }
    176. module.exports = WebpackMiddlewareMock;

    4. 修改webpackDevServer

    修改 config/webpackDevServer.config.js 文件

    引入 WebpackMiddlewareMock 中间件

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

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

    1. setupMiddlewares: (middlewares, devServer) => {
    2. const mockMiddleware = WebpackMiddlewareMock.use({
    3. rootDir: paths.appPath,
    4. });
    5. middlewares.unshift(mockMiddleware);
    6. return middlewares;
    7. },

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

    1. module.exports = {
    2. // 返回值是 String 类型
    3. "GET /api/name": "tom",
    4. // 返回值 Array 类型
    5. "POST /api/users": [
    6. { name: "foo", id: 0 },
    7. { name: "bar", id: 1 },
    8. ],
    9. "GET /api/users/:id": (req, res) => {
    10. res.send({
    11. params: req.params,
    12. });
    13. },
    14. // 返回值是 Object 类型
    15. "DELETE /api/users/1": { name: "bar", id: 1 },
    16. };

    5. 测试mock请求

    修改 App.js 文件

    1. import { useState } from "react";
    2. import axios from "axios";
    3. function App() {
    4. const [resultGet, setResultGet] = useState("");
    5. const [resultPost, setResultPost] = useState("");
    6. const [resultParams, setResultParams] = useState("");
    7. const [resultDelete, setResultDelete] = useState("");
    8. const handleGet = async () => {
    9. const res = await axios.get("/api/name");
    10. setResultGet(res.data);
    11. };
    12. const handlePost = async () => {
    13. const res = await axios.post("/api/users");
    14. setResultPost(JSON.stringify(res.data));
    15. };
    16. const handleParams = async () => {
    17. const res = await axios.get("/api/users/100");
    18. setResultParams(JSON.stringify(res.data));
    19. };
    20. const handleDelete = async () => {
    21. const res = await axios.delete("/api/users/1");
    22. setResultDelete(JSON.stringify(res.data));
    23. };
    24. return (
    25. <div className="App">
    26. <button onClick={handleGet}>"GET /api/name"button>
    27. <h2>{resultGet}h2>
    28. <hr />
    29. <button onClick={handlePost}>"POST /api/users"button>
    30. <h2>{resultPost}h2>
    31. <hr />
    32. <button onClick={handleParams}>"GET /api/users/:id"button>
    33. <h2>{resultParams}h2>
    34. <hr />
    35. <button onClick={handleDelete}>"DELETE /api/users/1"button>
    36. <h2>{resultDelete}h2>
    37. <hr />
    38. div>
    39. );
    40. }
    41. export default App;

    启动项目测试

  • 相关阅读:
    python毕业设计项目源码选题(18)教室实验室预约系统毕业设计毕设作品开题报告开题答辩PPT
    IDEA使用Docker插件
    基于PHP+MySQL游戏视频网站的设计与实现
    面向大规模队列,百万并发的多优先级消费系统设计
    [译]BNF 表示法:深入了解 Python 的语法
    oracle在where条件中关于索引字段的使用注意事项
    fisco Java-sdk 快速入门案例
    【JDBC】----封装工具类和ORM
    Linux——信号量(定义、示例、信号量接口)
    企业级信息化系统 ERP、OA、CRM、EAM、WMS、MES、PM
  • 原文地址:https://blog.csdn.net/d312697510/article/details/139832150