这是我参与更文挑战的第 2 天,活动详情查看: 更文挑战
这段时间在一直在使用飞冰开发,开发过程中觉得本地 Mock 数据的特性非常好用。于是就去学习了下具体的实现方法。
Mock 数据的基本使用
飞冰官方关于 Mock 数据的文档介绍的很详细了,我这里给尚未了解的小伙伴简单介绍下。
现有一个目录结构如下的项目
├── mock
│ └── index.js
├── package.json
└── sr
└── api.js
其中 mock/index.js
代码如下
export default {
// 同时支持 GET 和 POST
'/api/users/1': { data: {} },
'/api/foo/bar': { data: {} },
// 支持标准 HTTP
'GET /api/users': { users: [1, 2] },
'DELETE /api/users': { users: [1, 2] },
// 支持参数
'POST /api/users/:id': (req, res) => {
const { id } = req.params;
res.send({ id: id });
},
};
在我们启用 Mock 服务后,请求 mock/index.js
中定义的接口就会得到响应的返回。
Mock 数据的原理分析
当前飞冰的 Mock 功能是基于 Webpack DevServer 开发的,原理是:在 Webpack DevServer 创建的 Express 服务里增加一个中间件,通过这个中间件来拦截和处理请求。
飞冰官方已经把这个 Express 中间件发布为名称 webpack-dev-mock 的 npm package。 webpack-dev-mock 具体拦截请求的代码见下方:
app.use((req, res, next) => {
const match = mockConfig.length && matchPath(req, mockConfig);
if (match) {
debug(`mock matched: [${match.method}] ${match.path}`);
return match.handler(req, res, next);
} else {
return next();
}
});
其中 MockConfig
就是上文介绍的基于项目 mock 目录下的文件生成的配置。生成配置的 getConfig 代码片段如下:
function getConfig(rootDir: string, ignore: IIgnoreFolders) {
// get mock files
const mockFiles = glob.sync('mock/**/*.[jt]s', {
cwd: rootDir,
ignore,
}).map(file => path.join(rootDir, file));
// 省略。。。
const mockConfig = {};
mockFiles.forEach(mockFile => {
if (fse.existsSync(mockFile)) {
// disable require cache
Object.keys(require.cache).forEach(file => {
const winPathFile = winPath(file);
if (winPathFile === mockFile || winPathFile.indexOf(mockDir) > -1) {
debug(`delete cache ${file}`);
delete require.cache[file];
}
});
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
const mockModule = require(mockFile);
const mockData = mockModule.default || mockModule || {};
Object.assign(mockConfig, mockData);
} catch (err) {
console.log(err);
}
}
});
return mockConfig;
}
看到这里,我们应该已经能够了解本地数据 Mock 的基本原理了。但是肯定很多人对 webpack-dev-mock 是如何在 Webpack DevServer 中被使用还是有疑问的。
其实这是通过 devServer.before 来实现的,代码如下:
const webpackDevMock = require('webpack-dev-mock');
module.exports = (config, mock, context) => {
// dev mock
const { commandArgs, command } = context;
if (!commandArgs.disableMock && command === 'start' && mock) {
const originalDevServeBefore = config.devServer.get('before');
// replace devServer before function
config.merge({ devServer: {
before(app, server) {
// set cors before all served files
app.use((req, res, next) => {
res.set('Access-Control-Allow-Origin', '*');
next();
});
const mockIgnore = Object.prototype.toString.call(mock) === '[object Object]' && mock.exclude;
// keep mock server ahead of devServer.before
webpackDevMock(app, mockIgnore || []);
if (typeof originalDevServeBefore === 'function') {
originalDevServeBefore(app, server);
}
},
}});
}
};
我们可以看到,在 devServer 的 before 配置里用到了 webpack-dev-mock
对 Express 的请求做了处理。
实际应用
在了解原理后,我们也可以在自己的开发项目中添加本地 Mock 数据的功能。具体方法就是在 devServer.before 配置中使用 webpack-dev-mock 来处理 Express 服务器的请求。
使用 Vue Cli 的小伙伴甚至可以基于 webpack-dev-mock 来封装一个 Mock 数据的插件。