【NPM库】- 0x04 - Mock Data
时间:2022-07-23
本文章向大家介绍【NPM库】- 0x04 - Mock Data,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
- 1. Mock Data 基础
1.1. 是什么?
Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发更加独立自主,不会被服务端的开发所阻塞。
1.2. 常见 Mock 方式?
2. Mock Data 实现
2.1. 技术要点?
-
如何利用 webpack-dev-server 的 before 特性,进行请求拦截;
- 如何利用 body-parser 解析请求体(request body)
- 如何利用 pathToRegexp 匹配 URL 路径
-
如何利用 chokidar 实现 Mock Data 热更新。
- 如何利用 clear-module 清理 Mock Data 模块缓存。
- 如何利用 Mock.js 生成随机数据。
2.2. 效果预览?
2.3. Mock 文件编码方式
Mock 文件的编码方式,参考自 Umijs:
- 支持静态值
- 支持动态函数
2.4. devServer.before
- webpack-dev-server 底层基于 express。
- 核心是其 middleware 机制。
- devServer.before,会在其内部所有 middleware 执行之前触发。
- 这给了我们拦截、分析请求,并返回自定义 Mock Data 的机会。
2.5. body-parser
- Node.js body parsing middleware.
- Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
- body-parser provides the following parsers:
- JSON body parser
- Raw body parser
- Text body parser
- URL-encoded form body parser
a. 搭建一个 Demo(此时没使用 body-parser):
const express = require('express');
const app = express();
app.post('/login', (req, res) => {
console.log('********************')
console.log(req.body);
res.send("Hello WEBJ2EE.");
});
app.listen(3000, () => {
console.log('http://127.0.0.1:3000')
})
b. 使用 Postman 发送 POST 请求:
c. 不使用 body-parser 的情况下,直接获取 req.body,结果将是 undefined。
d. 配置 JSON 解析器。
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.post('/login', bodyParser.json(), (req, res) => {
console.log('********************')
console.log(req.body);
res.send("Hello WEBJ2EE.");
});
app.listen(3000, () => {
console.log('http://127.0.0.1:3000')
});
e. 使用 Postman 再次发送 JSON 数据,将得到执行结果。
备注:如果在模拟器上以非JSON格式发送,则会获得一个空的JSON对象
2.6. chokidar
A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.
a. 搭建一个 Demo:
const path = require("path");
const chokidar = require("chokidar");
chokidar.watch(path.resolve(__dirname, "ftp"))
.on("all", (event, path)=>{
console.log(event, path);
});
b. 看看其监听能力:
- 增加文件时,显示的事件名是add,并且显示对应的文件名;
- 修改文件内容时,显示的事件名是change,并且显示对应的文件名;
- 增加目录时,显示的事件名是addDir,并且显示对应的目录名;
- 删除文件时,显示的事件名是unlink,并且显示对应的文件名;
- 删除目录时,显示的事件名是unlinkDir,并且显示对应的目录名;
2.7. clear-module
Useful for testing purposes when you need to freshly import a module.
示例:
// foo.js
let i = 0;
module.exports = () => ++i;
const clearModule = require('clear-module');
require('./foo')();
//=> 1
require('./foo')();
//=> 2
clearModule('./foo');
require('./foo')();
//=> 1
2.8. 综合示例:Mocker.ts
import URL from 'url';
import PATH from 'path';
import { Request, Response, NextFunction, Application } from 'express';
import bodyParser from 'body-parser';
import * as toRegexp from 'path-to-regexp';
import { TokensToRegexpOptions, ParseOptions, Key } from 'path-to-regexp';
import clearModule from 'clear-module';
import chokidar from 'chokidar';
import color from 'colors-cli/safe';
export type MockerResultFunction = ((req: Request, res: Response, next?: NextFunction) => void);
export type MockerResult = string | number| Array<any> | Record<string, any> | MockerResultFunction;
export type MockerProxyRoute = Record<string, MockerResult> & {}
export type WatchFiles = Array<string>;
const pathToRegexp = toRegexp.pathToRegexp;
let mocker: MockerProxyRoute = {};
function pathMatch(options: TokensToRegexpOptions & ParseOptions) {
options = options || {};
return function (path: string) {
var keys: (Key & TokensToRegexpOptions & ParseOptions & { repeat: boolean })[] = [];
var re = pathToRegexp(path, keys, options);
return function (pathname: string, params?: any) {
var m = re.exec(pathname);
if (!m) return false;
params = params || {};
var key, param;
for (var i = 0; i < keys.length; i++) {
key = keys[i];
param = m[i + 1];
if (!param) continue;
params[key.name] = decodeURIComponent(param);
if (key.repeat) params[key.name] = params[key.name].split(key.delimiter)
}
return params;
}
}
}
// Merge multiple Mockers
function getConfig(watchFiles: WatchFiles) {
return watchFiles.reduce((mocker, file) => {
const mockerItem = require(file);
return Object.assign(mocker, mockerItem.default ? mockerItem.default : mockerItem);
}, {})
}
function watchMockFiles(watchFiles: WatchFiles){
const watcher = chokidar.watch(watchFiles.map(watchFile => PATH.dirname(require.resolve(watchFile))));
watcher.on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
watchFiles.forEach(file => cleanCache(file));
mocker = getConfig(watchFiles);
console.log(`${color.green_b.black(' Done: ')} Hot Mocker ${color.green(path.replace(process.cwd(), ''))} file replacement success!`);
} catch (ex) {
console.error(`${color.red_b.black(' Failed: ')} Hot Mocker ${color.red(path.replace(process.cwd(), ''))} file replacement failed!!`);
}
}
});
}
function cleanCache(modulePath: string) {
try {
modulePath = require.resolve(modulePath);
} catch (e) {}
var module = require.cache[modulePath];
if (!module) return;
clearModule(modulePath);
}
export default function (app: Application, watchFile: string | string[]) {
const watchFiles = Array.isArray(watchFile) ? watchFile : [watchFile];
if (watchFiles.some(file => !file)) {
throw new Error('Mocker file does not exist!.');
}
mocker = getConfig(watchFiles);
if (!mocker) {
return (req: Request, res: Response, next: NextFunction) => {
next();
}
}
watchMockFiles(watchFiles);
app.all('/*', (req: Request, res: Response, next: NextFunction) => {
const mockerKey: string = Object.keys(mocker).find((kname) => {
return !!pathToRegexp(kname.replace((new RegExp('^' + req.method + ' ')), '')).exec(req.path);
});
if (mocker[mockerKey]) {
let bodyParserMethd = bodyParser.json(); // 默认使用json解析
bodyParserMethd(req, res, function () {
const result = mocker[mockerKey];
if (typeof result === 'function') {
const rgxStr = ~mockerKey.indexOf(' ') ? ' ' : '';
req.params = pathMatch({ sensitive: false, strict: false, end: false })(mockerKey.split(new RegExp(rgxStr))[1])(URL.parse(req.url).pathname);
result(req, res, next);
} else {
res.json(result);
}
});
} else {
next();
}
});
return (req: Request, res: Response, next: NextFunction) => {
next();
}
}
核心逻辑截取自:mocker-api
- 利用 chokidar 监听 mock 文件变更,实现热更新特性。
- 监听到文件更新后,使用 clear-module 清理模块缓存,在下次请求拦截时,才能使更新后的 mock 文件生效。
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(32)-swfupload多文件上传[附源码]
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(31)-MVC使用RDL报表
- 43 Hot Flex and ActionScript 3.0 APIs, tips and tools for Autumn 2008
- 异步数据存储
- 谈谈基于SQL Server 的Exception Handling[中篇]
- C# AD(Active Directory)域信息同步,组织单位、用户等信息查询
- 如何仅使用TensorFlow C+来训练深度神经网络
- 捕获silverlight滚轮事件
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(30)-本地化(多语言)
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(29)-T4模版
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(28)-系统小结
- 用谷歌浏览器来当手机模拟器
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(27)-权限管理系统-分配用户给角色
- ASP.NET MVC5+EF6+EasyUI 后台管理系统-分配角色给用户
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 教育平台项目后台管理系统:接口文档
- 第四章--第二节:类
- 教育平台项目后台管理系统:介绍与搭建
- Python办公自动化之Excel做表自动化:全网最全,看这一篇就够了!
- Java学习笔记-spring-Bean实例化
- Java学习笔记-spring-Bean作用于
- 教育平台项目后台管理系统:课程信息模块
- 教育平台项目后台管理系统:课程内容模块
- 100 个 Python 小例子
- Entity Framework初体验
- Entity Framework 小知识(一)
- 教育平台项目前端:Vue.js 入门
- Entity Framework 约定
- 浏览器中的跨域问题与 CORS
- Entity Framework 小知识(二)