资讯专栏INFORMATION COLUMN

前端开发如何做好本地接口模拟

ACb0y / 882人阅读

摘要:前端开发如何做好本地接口模拟之前有写过一篇本地化接口模拟前后端并行开发,讲到过本地接口模拟,但不太细致。有哪些途径本地接口模拟一般分为工具层面和代码层面。因为本地接口模拟功能主要是针对的返回值为格式的异步请求,所以这种方式主要用文件。

前端开发如何做好本地接口模拟

之前有写过一篇 本地化接口模拟、前后端并行开发,讲到过本地接口模拟,但不太细致。这次细细的说说本地接口模拟。

1. 有什么好处

本地接口模拟最大的好处就是能够使前后端项目解耦,前端更专注于开发,减少线上调试,以此提升开发效率。

2. 有哪些途径

本地接口模拟一般分为工具层面和代码层面。

3. 工具层面

就工具层面而言,一般是由项目的构建工具提供的功能。比如,当我们用 webpack-dev-server、webpack-dev-middleware + browser-sync 等工具时,就可以向工具里添加本地接口模拟功能。

注:这里不讲解工具如 webpack-dev-serverwebpack-dev-middleware + browser-sync 等的用法,如有需要,可以自己去了解一下

下面以 webpack-dev-server 为例进行讲解,其他工具类似。

3.1 静态文件

最简单的,我们可以用静态 json 文件做本地接口模拟功能。

|-- /                        # 项目根目录
  |-- mock/                  # 模拟数据目录(可以自定义)
    |-- 1.json
    |-- 2.json
    |-- ...
    
  |-- ...                    # 其他文件                 

然后用 webpack-dev-server 以项目根目录为基地址来开启本地开发调试,在页面中就可以这样访问:

fetch("/mock/1.json");       // 访问 1.json 
fetch("/mock/2.json");       // 访问 2.json

# 可以将 fetch 换成其他请求方式 

这种方式可以访问项目中所有的文件,不光是 json 文件,其他的如 htmljscss 之类的文本文件、如图片之类的二进制文件也可以访问。另外,只要文件有更新,刷新浏览器页面就可以重新获取新的文件,没有缓存。

因为本地接口模拟功能主要是针对的返回值为 json 格式的异步请求,所以这种方式主要用 json 文件。

这种方式是最简单、快捷、使用难度最低的方式。

3.2 动态注册接口

使用静态文件做本地接口模拟功能主要存在以下的一些问题:

静态文件只能以 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 }],
  }),
};

注:

上面的写法只是示例,可以自定规范、书写格式等

可以用 mockjs 库来帮助生成假数据

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 方法、参数、返回值等,所以同样的代码既可以在本地运行,也可以在服务器上运行。

所以,这也是比较推荐的方式。

注:上面的代码只是演示构建过程,并不保证可以运行
3.3 使用代理

这种方式是把本地模拟文件写在另一个多带带项目里,然后使用使用代理的方式,访问模拟接口。

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

这种方式可以统一管理多个项目的数据模拟文件,多个项目可以共享一些模拟数据。

3.4 线上接口模拟

有些时候,当我们在产品环境的时候(在线上)也可能想用模拟数据(比如APP、微信小程序、用于演示的 web 应用等),或者需要一个线上的地方来统一管理模拟数据时,就需要线上接口模拟了。

线上接口模拟拥有完备的 UI 操作界面,可以添加多个用户、多个团队、多个仓库,可以生成为每一个请求参数添加类型限定、描述,为响应数据字段添加描述等。

因为是在线上的模拟数据,所以在任何地方都可用,不管是本地开发,还是线上调试、演示,都是可用的。

比较有名的线上接口模拟工具有:

easy-mock:线上演示地址 https://easy-mock.com/

RAP / rap2-delos + rap2-dolores:阿里出品,线上演示地址 http://rap2.taobao.org/

3.4.1 easy-mock

环境需求:Node.js (>= v8.9) & MongoDB (>= v3.4) & Redis(>= v4.0)

安装步骤请参考官方的文档 easy-mock#quick-start

easy-mock 主要提供了以下的一些功能:

支持接口代理

支持快捷键操作

支持协同编辑

支持团队项目

支持 RESTful

支持 Swagger | OpenAPI Specification (1.2 & 2.0 & 3.0)

基于 Swagger 快速创建项目

支持显示接口入参与返回值

支持显示实体类

支持灵活性与扩展性更高的响应式数据开发

支持自定义响应配置(例:status/headers/cookies)

支持 Mock.js 语法

支持 restc 方式的接口预览

3.4.2 RAP / rap2-delos + rap2-dolores

环境需求:Node.js (>= v8.9) & MySQL (>= v5.7) & Redis(>= v4.0)

RAP 目前有两个版本,第一个版本的 RAP 已经被官方废弃了,建议用第二个版本。

RAP2 分成了两个包:

rap2-delos:后端数据 API 服务器

rap2-dolores:前端静态资源

RAP2 的安装步骤要麻烦一些,rap2-delos 可以参考官方文档 rap2-delos#部署、非官方rap2-delos部署文档,rap2-dolores 可以参考官方文档 rap2-dolores#deployment-部署。

RAP2 提供了与 easy-mock 类似的功能,但比 easy-mock 要更强大一些,当然也要复杂一些,比如:

对请求参数支持更完备,比如 headersquery paramsbody params

对响应数据支持更完备,比如可以为每个字段添加描述、限定类型等

3.4.3 两者之间比较

RAP2 比 easy-mock 要更强大一些,但也要复杂一些,所以追求功能完备的可以用 RAP2,追求简单快捷的可以用 easy-mock

4. 代码层面

从上面可以看出,除了第二种方式 动态注册接口 之外,其他的方式都不能做到完全模拟服务器环境,至少服务器接口地址与本地模拟地址不一样,这就有一个问题:每次上线前都得改成服务器地址。

另外,前端与后端对接的过程中也总是难免会遇到一些问题:

前端传的参数与后端所需的参数难以保持一致:比如分页,前端定的 page、从 1 开始,后端定的 pageNum、从 0 开始

前端需要的数据与后端返回的数据相差很大,需要对返回的数据做处理

后端可能更改字段名或者数据类型,导致前端要查找、并更新相应的代码

一种好的、解决这些问题的方式是对应用进行分层、把异步请求进行隔离封装。

我一般会用 see-fetch、see-ajax 对异步请求进行隔离封装。

注:see-fetch 是对 window.fetch 的封装,see-ajax 是对 XMLHttpRequest 对象的封装。

4.1 以 see-fetch 为例进行说明:

可以在代码中设置多个内部环境,然后针对不同的外部环境设置不同的内部环境(如:本地环境、线上环境等),这样就可以做到不改代码,只改一个环境值。

如果搭配 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

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100739.html

相关文章

  • 前端如何高效的与后端协作开发

    摘要:移动端应用一般都运行在微信浏览器中中手机浏览器中。因为微信浏览器是定制的浏览器,一般的远程调试方式都不可用,需要配合特定的工具,如微信开发者工具。 前端如何高效的与后端协作开发 1. 前后端分离 前端与后端的分离,能使前端的开发脱离后端的开发模式,拥有更大的自由度,以此便可做前端工程化、组件化、单页面应用等。 可以参考:前后端分离、web与static服务器分离 2. 尽量避免后端模板...

    rozbo 评论0 收藏0
  • 前端如何高效的与后端协作开发

    摘要:移动端应用一般都运行在微信浏览器中中手机浏览器中。因为微信浏览器是定制的浏览器,一般的远程调试方式都不可用,需要配合特定的工具,如微信开发者工具。 前端如何高效的与后端协作开发 1. 前后端分离 前端与后端的分离,能使前端的开发脱离后端的开发模式,拥有更大的自由度,以此便可做前端工程化、组件化、单页面应用等。 可以参考:前后端分离、web与static服务器分离 2. 尽量避免后端模板...

    learning 评论0 收藏0
  • 实现前后端分离的心得

    摘要:实现前后端分离的心得对目前的来说,前后端分离已经变得越来越流行了,越来越多的企业网站都开始往这个方向靠拢。前后端工作分配不均。 实现前后端分离的心得 对目前的web来说,前后端分离已经变得越来越流行了,越来越多的企业/网站都开始往这个方向靠拢。那么,为什么要选择前后端分离呢?前后端分离对实际开发有什么好处呢? 为什么选择前后端分离 在以前传统的网站开发中,前端一般扮演的只是切图的工作...

    zilu 评论0 收藏0
  • 99%的程序都没有考虑的网络异常

    摘要:本文由云社区发表绝大多数程序只考虑了接口正常工作的场景,而用户在使用我们的产品时遇到的各类异常,全都丢在看似的中。在面板,还可以对请求进行暂停延迟等网络异常的模拟。小程序实现最后,留一道思考题。 本文由云+社区发表 绝大多数程序只考虑了接口正常工作的场景,而用户在使用我们的产品时遇到的各类异常,全都丢在看似 ok 的 try catch 中。如果没有做好异常的兼容和兜底处理,会极大的影...

    HollisChuang 评论0 收藏0
  • 如何构建大型的前端项目

    摘要:如何构建大型的前端项目搭建好项目的脚手架一般新开发一个项目时,我们会首先搭建好一个脚手架,然后才会开始写代码。组件化一般分为项目内的组件化和项目外的组件化。 如何构建大型的前端项目 1. 搭建好项目的脚手架 一般新开发一个项目时,我们会首先搭建好一个脚手架,然后才会开始写代码。一般脚手架都应当有以下的几个功能: 自动化构建代码,比如打包、压缩、上传等功能 本地开发与调试,并有热替换与...

    lykops 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<