以前有写过一篇 本地化接口模拟、先后端并行开发,讲到过本地接口模拟,但不太细致。此次细细的说说本地接口模拟。css
本地接口模拟最大的好处就是可以使先后端项目解耦,前端更专一于开发,减小线上调试,以此提高开发效率。html
本地接口模拟通常分为工具层面和代码层面。前端
就工具层面而言,通常是由项目的构建工具提供的功能。好比,当咱们用 webpack-dev-server、webpack-dev-middleware + browser-sync 等工具时,就能够向工具里添加本地接口模拟功能。node
注:这里不讲解工具如webpack-dev-server
、webpack-dev-middleware
+browser-sync
等的用法,若有须要,能够本身去了解一下
下面以 webpack-dev-server
为例进行讲解,其余工具相似。webpack
最简单的,咱们能够用静态 json
文件作本地接口模拟功能。git
|-- / # 项目根目录 |-- mock/ # 模拟数据目录(能够自定义) |-- 1.json |-- 2.json |-- ... |-- ... # 其余文件
而后用 webpack-dev-server
以项目根目录为基地址来开启本地开发调试,在页面中就能够这样访问:github
fetch('/mock/1.json'); // 访问 1.json fetch('/mock/2.json'); // 访问 2.json # 能够将 fetch 换成其余请求方式
这种方式能够访问项目中全部的文件,不光是 json
文件,其余的如 html
、js
、css
之类的文本文件、如图片之类的二进制文件也能够访问。另外,只要文件有更新,刷新浏览器页面就能够从新获取新的文件,没有缓存。web
由于本地接口模拟功能主要是针对的返回值为 json
格式的异步请求,因此这种方式主要用 json
文件。ajax
这种方式是最简单、快捷、使用难度最低的方式。json
使用静态文件作本地接口模拟功能主要存在如下的一些问题:
get
方法访问因此,多数状况下,都会采用动态注册接口的方式作本地接口模拟功能。
这种方式是用 js
文件编写一系列的 路由 => 响应
映射,而后动态的把定义好的接口注册到工具实例中。
目录结构:
|-- / # 项目根目录 |-- mock/ # 模拟数据目录(能够自定义) |-- user.js |-- home.js |-- ... |-- ... # 其余文件
示例 mock
文件的写法(能够自定规范,下面只是演示):
# mock/user.js module.exports = { 'GET /user/profile': { ... }, // 直接返回一个对象 'POST /user/update': (req, res) => { ... }, // 根据 `req, res` 的自定义响应 ... }; # mock/home.js const mockjs = require('mockjs'); module.exports = { 'GET /home/list': mockjs.mock({ // 用 mockjs 辅助生成假数据 'list|1-10': [{ 'id|+1': 1 }], }), };
注:
webpack-dev-server
的相关配置:
# webpack.config.js const beforeDevServer = app => { // 在这里读取 mock 目录下的全部文件,按照必定的规范和格式,载入动态接口 // 好比: app.get('/user/profile', function(req, res) { res.json({ ... }); // 返回文件中定义的 `GET /user/profile` 的值 }); app.post('/user/update', function(req, res) { handle(req, res); // handle:文件中定义的 `POST /user/update` 的自定义处理函数 }); ... }; module.exports = { //... devServer: { before: beforeDevServer, } };
而后用 webpack-dev-server
开启本地开发调试,在页面中就能够这样访问:
fetch('/user/profile'); // 访问 /user/profile fetch('/user/update', {method: 'post', body: { ... }}); // 访问 /user/update fetch('/home/list'); // 访问 /home/list # 能够将 fetch 换成其余请求方式
通常来讲,咱们还会用上 chokidar 来监听 mock
目录下的文件变更,来更新路由及其响应,以此可以作到每次访问到的都是最新的资源(由于 node
针对某个模块只会加载一次)。
const chokidar = require('chokidar'); const watcher = chokidar.watch('./mock'); watcher.on('change', path => { // 先清除模块缓存,保证加载最新的资源 if (require.cache[path]) delete require.cache[path]; const mapObj = require(path); // 接下来把映射对象 mapObj 从新映射到 app 中 ... });
这种方式比较复杂,尤为是对项目搭建者要求比较高,须要对相关工具备深刻的了解,好在社区已经有封装好的工具:roadhog。
但这种方式对使用者是很棒的,由于可以彻底模拟服务器接口,包括接口名、HTTP 方法、参数、返回值等,因此一样的代码既能够在本地运行,也能够在服务器上运行。
因此,这也是比较推荐的方式。
注:上面的代码只是演示构建过程,并不保证能够运行
这种方式是把本地模拟文件写在另外一个单独项目里,而后使用使用代理的方式,访问模拟接口。
mock
项目(以 koa 为例):
const Koa = require('koa'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); router.get('/api/user/profile', (ctx, next) => { ctx.body = { ... }; }); router.post('/api/user/update', (ctx, next) => { // ... }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(3000);
app
应用项目:
webpack-dev-server
的相关配置:
# webpack.config.js module.exports = { //... devServer: { proxy: { '/api': 'http://localhost:3000' // 把全部 `/api` 开头的接口都代理到 `mock` 项目中 } } };
而后用 webpack-dev-server
开启本地开发调试,在页面中就能够这样访问:
fetch('/api/user/profile'); // 访问 mock 项目的 /api/user/profile fetch('/api/user/update', {method: 'post', body: { ... }}); // 访问 mock 项目的 /api/user/update # 能够将 fetch 换成其余请求方式
通常来讲,咱们还会用上 nodemon 来监听 mock
项目中的文件变更,自动重启 mock
应用程序,以此可以作到每次访问到的都是最新的资源(由于 node
针对某个模块只会加载一次)。
nodemon app.js
这种方式能够统一管理多个项目的数据模拟文件,多个项目能够共享一些模拟数据。
有些时候,当咱们在产品环境的时候(在线上)也可能想用模拟数据(好比APP、微信小程序、用于演示的 web 应用等),或者须要一个线上的地方来统一管理模拟数据时,就须要线上接口模拟了。
线上接口模拟拥有完备的 UI 操做界面,能够添加多个用户、多个团队、多个仓库,能够生成为每个请求参数添加类型限定、描述,为响应数据字段添加描述等。
由于是在线上的模拟数据,因此在任何地方均可用,不论是本地开发,仍是线上调试、演示,都是可用的。
比较有名的线上接口模拟工具备:
环境需求:Node.js (>= v8.9) & MongoDB (>= v3.4) & Redis(>= v4.0)
安装步骤请参考官方的文档 easy-mock#quick-start
easy-mock
主要提供了如下的一些功能:
支持 Swagger | OpenAPI Specification (1.2 & 2.0 & 3.0)
环境需求:Node.js (>= v8.9) & MySQL (>= v5.7) & Redis(>= v4.0)
RAP 目前有两个版本,第一个版本的 RAP 已经被官方废弃了,建议用第二个版本。
RAP2 分红了两个包:
RAP2 的安装步骤要麻烦一些,rap2-delos
能够参考官方文档 rap2-delos#部署、非官方rap2-delos部署文档,rap2-dolores
能够参考官方文档 rap2-dolores#deployment-部署。
RAP2 提供了与 easy-mock
相似的功能,但比 easy-mock
要更强大一些,固然也要复杂一些,好比:
headers
、query params
、body params
RAP2 比 easy-mock
要更强大一些,但也要复杂一些,因此追求功能完备的能够用 RAP2,追求简单快捷的能够用 easy-mock
。
从上面能够看出,除了第二种方式 动态注册接口
以外,其余的方式都不能作到彻底模拟服务器环境,至少服务器接口地址与本地模拟地址不同,这就有一个问题:每次上线前都得改为服务器地址。
另外,前端与后端对接的过程当中也老是不免会遇到一些问题:
page
、从 1 开始,后端定的 pageNum
、从 0 开始一种好的、解决这些问题的方式是对应用进行分层、把异步请求进行隔离封装。
我通常会用 see-fetch、see-ajax 对异步请求进行隔离封装。
注:see-fetch 是对 window.fetch
的封装,see-ajax 是对 XMLHttpRequest
对象的封装。
能够在代码中设置多个内部环境,而后针对不一样的外部环境设置不一样的内部环境(如:本地环境、线上环境等),这样就能够作到不改代码,只改一个环境值。
若是搭配 define-plugin,连环境值都不须要改,直接由 define-plugin
在运行的过程当中指定。
import seeFetch from 'see-fetch'; seeFetch.setEnv(0/1/2/3); seeFetch.setEnv(__SEE_ENV__); // __SEE_ENV__ 由 define-plugin 运行中指定
配置一个异步请求:
seeFetch.config(name, { // 定义一个名为 name 的异步请求(下面的配置能够是多环境,每一个环境能够设置不一样的值) method, // 当前请求使用什么 http 方法 stringify, settings, url, // 当前请求 url 地址 req, // 请求参数键名的映射,好比 `page => pageNum` pre, // 操做请求参数,好比 page 从 1 开始改为从 0 开始 refactor, // 重构响应数据,如字段重命名、类型转换等 post, // 操做响应数据,以把数据转换成本身所须要的数据 implement, // 自定义请求,好比后端返回一个模板字符串,而不是接口 });
发起访问:
seeFetch(name, params).then(result => { // 这里的 result 是通过格式化后的最终数据 });
从上面能够看出:一个请求的不肯定性都被封装到了配置中,不论是接口地址更新、前端请求参数与后端不一致、后端响应数据与前端所需的差别很大等,均可以在配置中进行操做,而丝绝不须要改其余地方的代码。
这样,若是后端接口有什么改动的,只须要找到配置文件进行更新,而不用在项目中找哪里使用了这个接口。
如此,既能很好的使用本地数据模拟,也能够从容应对后端接口的改动,便能事半功倍。
更多博客,查看 https://github.com/senntyou/blogs
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)