Foxman, 基于微核架构的 Mock 解决方案
本文来自 网易云社区 。
Foxman ⇗ 是一个使用 Node.js 开发的命令行工具,定位是一个可扩展的 Mock Server,帮助前端开发者轻松、独立、高效地进行前端开发和完成后续的联调工作。
他不是一款静态文件响应工具, 假如你只需要一款轻量的 Node.js 开发服务器,推荐你使用 puer ⇗ 或 webpack-dev-server ⇗。
github 地址: https://github.com/kaola-fed/foxman

背景
作为前端开发的我们,在实际的开发场景中会遇到以下问题:
- 环境:进行本地开发,需要起后端环境(Tomcat),对于新人来说,需要大量时间熟悉;熟练的人遇到某些确实存在的问题,也要花时间去解决,耗费大量前端开发的时间;
- 流程:前端开发先开发 html,再将 html 改写成指定的模板语法,影响开发效率;
- 接口:
- 接口定义使用聊天工具发送,前端开发时不好理解接口字段,影响开发效率;
- 接口变更需要重新编写文档,并重新发送,影响开发效率;
- 文档散落,影响接口维护;
- 联调:
- 联调过程很复杂,尤其是没有做热部署的Java工程,改视图还需要重启Tomcat,影响前端联调效率;
- 效益:
- 前后端对接的方式,期望纯粹的 JSON 交换。不过现实情况,是依赖后端的模板引擎,导致前端理解接口存在一定的障碍;
以上问题的存在,才产生了 Foxman 这个项目。
影响
从 考拉前端 使用情况来看,在接入 Foxman 后开发效率得到一定提升,主要体现在以下方面:
- 前端开发者不再需要在本地起 Tomcat 服务,新人也无需熟悉本地启动环境;而启动一个 Foxman 所需要的时间,在 5s 以内;
- 前端开发者更加有意识地去与后端定义接口,因为接口定义会落实到具体的 mock 数据上;
- Mock 功能,使得前端开发者在开发阶段几乎可以是自治、无打扰的情况(产品不改需求的前提下);
- Foxman 提供 Living Reload 的功能 - 页面开发过程中,修改 html 和 js 会通知浏览器 reload 页面;修改 css 会通知浏览器只 reload 样式,提升了开发体验,节省了人肉刷新耗费的时间。
- Foxman 提供 Processors 的功能 - 即时编译的设定,更好地兼容无 webpack 构建的场景;
- 联调阶段,由于 Foxman 提供 了 Proxy 功能,使前端开发可以再本地调试模板和 javascript,避免了修改提交,再重新部署服务器的时间耗费,大大提升联调效率与体验;
核心概念
容器 - Foxman 核心提供了一个挂载插件的容器,并且提供方法供插件提供或调用的服务。实现上,使用了IoC(依赖查找)、插件化等架构设计的思想。
插件 - Foxman 所有具体的功能都使用插件实现。插件的作用是实现本身需求,并提供服务供其他插件使用。
服务 - 服务是架设于 容器 与 插件 之上的概念,容器 提供方法供 插件 注册或调用服务。
在这样的体系下,你可以轻松地编写 Foxman 的插件,并调用已有插件的服务。所以,完全不需要担心,Foxman 会不适合你的项目,因为你完全可以根据自己的需求来定制你所需要的Foxman。
安装
NPM
$ npm i -g foxman@lastest # 无梯子用户,推荐使用 cnpm
⚠️ Foxman 采用 es6 语法的大部分特性编写,要求使用 Node.js 版本不低于 v6.4.0
编写配置文件
module.exports = {
port: 9000,
secure: false,
statics: [
'./src/'
],
routes: [
{
method: 'GET',
url: '/ajax/index.html',
sync: false,
filePath: 'foo.bar'
}
],
engineConfig: {},
viewRoot: './views/',
extension: 'ftl',
syncData: './syncData/',
asyncData: './ajax/',
plugins: [],
processors: [
{ match: '/src/css/*.css', pipeline: [], locate( reqUrl ) {} }
],
proxy: [
{ name: 'pre', host: 'm.kaola.com', ip: '1.1.1.1', protocol: 'http' }
]
}
这是一份基础的 Foxman 的配置文件,可以发现大部分字段都给 Server 用的,比如:
- port - Server 监听的端口
- secure - 是否启用 https
- statics - 静态资源配置
- routes - 路由列表
- engineConfig - 模板引擎配置项
- viewRoot - 模板根目录
- extension - 模板扩展名
- syncData - 同步数据根目录
- asyncData - 异步数据根目录
以及一些特殊的字段,后面我们会重点介绍:
- proxy - 联调阶段,同步数据与异步数据的转发至后端主机或测试服务器
- processors - Runtime Compiler 的配置
- plugins - 插件配置
更详细的 Foxman 配置,点击此处 ⇗
启动
在编写完 foxman.config 的目录下,执行命令即可启动 Foxman :
$ foxman
设计理念
插件体系
Foxman 的外置插件可以在配置文件中灵活载入:
...
plugins: [
new RouteDisplay(),
new MockControl({}),
new Automount({}),
new WebpackDevServer({}),
]
...
而所有的内置功能,其实也是依托于插件展开。每个 Foxman 插件,需要实现一些方法,用于装载入 Foxman 容器时,做一些登记工作:
class Plugin {
constructor() {
// 初始化自身需要的属性
}
name() { // 定义插件的名字,如果没有该字段,会使用 constructor.name
return 'name';
}
service() { // 提供给其他插件的服务
return {
foo() {
return 'bar';
}
}
}
init({getter, service}) {
const use = service('service.use');
}
}
容器与依赖查找
容器的设定,离不开 IoC(控制反转)的概念。
实现 IoC,惯用的一种方案是依赖注入 (Dependency Injection) ,用于运行时被动地接收依赖的对象,早期的 Foxman 是根据 DI 的方式实现插件化的;
另一种方案是依赖查找 (Dependency Lookup) - 与 DI 相比更加主动,主动得调用框架提供的方法来获取依赖,获取时提供相关的配置文件路径 或 keypath 等信息。
Foxman 核心提供了 use 和 start 两个方法:
- use - 注册 Plugin 及 service
- start - 执行 Plugin 的 init 方法,传入 service/getter 方法,供其依赖查找
// core.js
class Core {
use() {
// 1. 注册 Plugin 进入容器;
// 2. 在容器中登记 Plugin 提供的 service
} start() {
// 1. 循环 Plugin 执行 init 方法, 注入 getters, service 等方法,用于获取其他插件的配置或是服务
// 2. 如果插件执行了 this.pending 方法,则等待异步操作完成。
}
}
// app.js
const core = new Core(); core.use(new Plugin({}));
// 1. 执行 Plugin constructor
// 2. 注册 Plugin 进入容器
// 3. 在容器中登记 Plugin 提供的 service core.start();
// 执行 Plugin 的 init 方法,会在参数中注入的 getters 和 service 方法,用于插件依赖查找,
具体的实现细节,感兴趣的同学可以 查看源码 ⇗
功能模块
Server模块
基于 Node.js Server 框架 koa@1.x 构建,Server 的职责便是渲染模板、响应异步数据,以及在页面插入一些特定的脚本。
整个 Server 的启动分为三个阶段:
- 初始化 - 设置配置,设置路由,以及初始化 Koa 对象;
- 装载中间件 - 初始化中间件队列;
- 启动服务 - 启动 Server,并建立 websocket 服务器,用于与浏览器的通信。
在 Server 启动后,请求进入 Server 时,会经历中间件的处理,这个过程又能分为 3 个阶段:
- 请求分析,及确定响应方式,在请求的 context 上,生成 dispatcher 对象,用于在步骤 3 中确定以何种方式进行响应(同步 or 异步,模板路径 or mock 数据路径);
- 由插件装载的中间件对请求进行处理(取决于具体使用的插件),这个阶段可以对 dispatcher 对象进行修改,以完成插件所期望的渲染方式;
- 请求响应,根据请求的类型,分为以下几种方式
- 同步请求 - 交给 Foxman-Engine 进行渲染,(注入一些 script 脚本,并且在页面上追加同步数据,使得浏览器 console 中输入 window.FOXMAN_SYNC_DATA 即可获得 )
- 异步请求 - 默认 json 方式展示,如需要 jsonp 响应,或是要自由控制响应方式,请使用插件 @foxman/plugin-mockcontrol ⇗
- 文件夹请求 - 展示文件夹内的文件列表
- 静态资源 - 响应静态资源
Server模块 提供其他插件一些关于 Server 相关的服务,可以供其他插件调用,比如:
- injectScript - 允许其他插件在同步接口中插入 javascript 脚本
- eval - 允许其他插件执行 js 代码
- livereload - 允许其他插件通知浏览器 reload
- use - 允许其他插件给 server 加入中间件
- registerRouterNamespace - 允许其他插件新增路由,使用命名空间可以保证不同的插件的路由不会相互干扰
Foxman 的内置的 Mock Data 编写方式使用最原始的 JSON 字符串。
没有使用 MockJS 等库的原因是,原始的 JSON字符串,使用者可以对模拟数据的完全掌控。
有特殊需求可以使用插件 @foxman/plugin-mockcontrol ⇗对响应进行额外控制。
整合 NEI
NEI ⇗是我们网易开发的一个接口定义平台。
foxman 接入 nei 非常简单, 在 foxman.config.js 中配置 nei key 即可:
...
nei: {
key: 'xxx' // nei key
}
...
首次运行会自动同步 NEI 接口。
当需要更新本地 nei 接口时,使用以下命令:
$ foxman -U
Template 渲染引擎
模板解析模块,具有特定接口,完成模板渲染需求。
var engine = require('@foxman/engine-arttemplate');
...
engine: engine,
engineConfig: { // 取决于具体的模板引擎
bail: true,
compileDebug: true,
imports: renderImports,
debug: false,
cache: false,
}
...
目前支持的模板引擎有:
- Freemaker - @foxman/engine-freemarker ⇗
- Art-template - @foxman/engine-arttemplate ⇗
- Handlebars - @foxman/engine-handlebars ⇗
假如没有你需要的,你也可以自行开发一款 Foxman 的模板引擎解析器,只需要实现一个特定的接口,基本结构如下。
const template = require('xxx-template');
class TemplateEngin {
constructor(viewRoot, engineConfig) {
// 初始化配置
}
parse(path, mockData) {
// 返回一个 Promise,Promise 的返回是处理后的接口
return Promise.resolve(template(path, mockData));
}
}
具体实现,参考 @foxman/engine-arttemplate ⇗
Proxy
使用本地的模板,结合远程端的数据来拼装页面。
代理的原理:
- Foxman 接收到用户的代理需求时,将请求转发给后台服务器,并带上特殊的请求头(X-Special-Proxy-Header: foxman);
- 后端接收到 Foxman 的代理请求后,要求以 JSON 的方式将页面的同步数据返回;
- Foxman 接收到服务端的响应数据后,结合本地的模板来实现模板渲染的需求,并响应给用户。
代理的设定,使得我们可以在本地的环境下调试测试环境的场景,发现存在前端的 bug 也能轻松修复,不再需要重复的部署测试服务器。
来接触下 Foxman Proxy 的实际配置:
...
proxy: [{
name: 'pre',
host: 'm.kaola.com', // 用于 nginx 转发到制定应用
ip: '1.1.1.1', // 目标的 IP 地址
protocol: 'http' // 协议
}]
...
完成上述配置后,使用者输入以下命令启动 Foxman,即可代理至远程服务器
$ foxman -P pre # pre 为 配置的 proxy name
Processors
Processors 是 Runtime Compiler 的设定,在接收到静态资源请求时,才去即时地编译前端资源(sass/less/mcss/autoprefixer),主要目的是兼容无 webpack 构建的开发场景。如已使用 webpack,则推荐使用插件 @foxman/plugin-webapck-dev-server
举例介绍 mcss 的即时编译配置
const Mcss = require('@foxman/processor-mcss');
const AutoPrefixer = require('@foxman/processor-autoprefixer');
...
processors: [
{
match: '/src/css/**.css', // 拦截该请求
pipeline: [ // pipe 式的处理
new Mcss({
paths: []
}),
new AutoPrefixer({
cascade: false,
browsers: '> 5%'
})
],
locate(reqPath) { // 根据请求路径,定位到在系统中具体路径
return path.join(__dirname + reqPath.replace(/css/g, 'mcss'));
}
}
],
...
假如没有你需要的,你也可以自行开发一款 Foxman 的 Processor ,只需要实现一个特定的接口,基本结构如下:
const mcss = require('mcss');
class Processor {
constructor(options) {
// 初始化解析器的参数
}
locate(reqPath) { // 根据请求路径找到文件在系统中的位置
return reqPath.replace(/\.css$/g, '\.mcss');
}
*handler({ raw, filename }) {
return yield new Promise((resolve, reject) => {
// 在这里 进行 parse 操作,如 sass | less 的解析操作
return {
dependencies, content
// 该文件依赖,及内容
}
})
}
}
具体实现,参考 @foxman/processor-mcss ⇗
结束
最后,如果你对 Foxman 的开发感兴趣,欢迎一起参与到开发当中。
感谢阅读!
本文已由作者许骏宇授权网易云社区发布。
Foxman, 基于微核架构的 Mock 解决方案的更多相关文章
- atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener
atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs ...
- 基于AgileEAS.NET企业应用平台实现基于SOA架构的应用整合方案-开篇
开篇 系统架构的文章,准备在这段时间好好的梳理和整理一下,然后发布基于AgileEAS.NET平台之上的企业级应用架构实践,结合具体的案例来说明AgileEAS.NET平 台之上如何进行系统的逻辑架构 ...
- COS 数据湖最佳实践:基于 Serverless 架构的入湖方案
01 前言 数据湖(Data Lake)概念自2011年被推出后,其概念定位.架构设计和相关技术都得到了飞速发展和众多实践,数据湖也从单一数据存储池概念演进为包括 ETL 分析.数据转换及数据处理的下 ...
- 如何基于LSM-tree架构实现一写多读
一 前言 PolarDB是阿里巴巴自研的新一代云原生关系型数据库,在存储计算分离架构下,利用了软硬件结合的优势,为用户提供具备极致弹性.海量存储.高性能.低成本的数据库服务.X-Engine是阿里巴 ...
- 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)
通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...
- 常用 Gulp 插件汇总 —— 基于 Gulp 的前端集成解决方案(三)
前两篇文章讨论了 Gulp 的安装部署及基本概念,借助于 Gulp 强大的 插件生态 可以完成很多常见的和不常见的任务.本文主要汇总常用的 Gulp 插件及其基本使用,需要读者对 Gulp 有一个基本 ...
- 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一)
相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常用 Gulp 插件汇总 ...
- 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)
前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...
- Yii2 基于RESTful架构的 advanced版API接口开发 配置、实现、测试 (转)
环境配置: 开启服务器伪静态 本处以apache为例,查看apache的conf目录下httpd.conf,找到下面的代码 LoadModule rewrite_module modules/mod_ ...
随机推荐
- 黑暗之光 Day1
1. 设置鼠标指针图标 Build Setting -> Player Setting 2. 添加雾的效果 Window->Lighting->Settings. 3. 任意键按下事 ...
- Elasticsearch-PHP 概述
最近在学习使用Elasticsearch,并且是和PHP一起使用的,看到了Elasticsearch-PHP,其实是Elasticsearch为PHP提供的客户端,那么我们来学习一下API文档,如何在 ...
- MySQL数据库篇之索引原理与慢查询优化之二
接上篇 7️⃣ 正确使用索引 一.索引未命中 并不是说我们创建了索引就一定会加快查询速度,若想利用索引达到预想的提高查询速度的效果, 我们在添加索引时,必须遵循以下问题: #1 范围问题,或者说条件 ...
- linux系统中的进程
一.fork 在类unix系统中,我们所执行的任何程序,都是由父进程(parent process)所产生出来的一个子进程(child process),子进程在结束后,将返回到父进程去.此一现象被称 ...
- 28-组合数(dfs)
http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=32 组合数 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 ...
- eclipse基础环境配置和svn、maven的配置以及maven的安装
安装eclipse和基础配置 第一步:解压eclipse安装包,直接解压就可以,绿色版安装 第二步:启动eclipse,注意这里的eclipse需要依赖jdk,并且版本需要匹配,否则启动会出 现问题. ...
- ROS Learning-032 (提高篇-010 Launch)Launch 深入研究 --- (启动文件编程)ROS 的 XML语法简介
ROS 提高篇 之 Launch 深入研究 - 01 - 启动文件的编程 - ROS 的 XML语法简介 我使用的虚拟机软件:VMware Workstation 11 使用的Ubuntu系统:Ubu ...
- java基础知识汇总(持续更新中....)
1.java四大特性:抽象.继承.封装,多态 构造函数: http://blog.csdn.net/qq_33642117/article/details/51909346 2.java数据基本类型: ...
- Java研发书单
Java研发书单 计算机基础:<深入理解计算机系统><计算机网络> 网络方面:<TCP/IP协议卷一><unix网络编程卷一>(部分章节,JAVA主要是 ...
- tp5写日志