项目诞生背景:由于后端使用的后端框架并不是一成不变的,以前的版本并不是按照现在通用的Restful
风格去编写的,导致,前端想要编写Mock
变得十分困难。虽然在内部已经有Eolinker
这样的平台可以提供 mock,但是始终在线上,而且由于之前的限制,mock 就无法进行编写。
1 所以为了兼容,新旧项目,我们设想了一种情景,加入我有一个环境,可以让我自由编写,在每个不需要管理常用的中间件?我不用管理项目环境变量?能够跟Eoliner
或者Postman
一样拥有全局的前置或者后置脚本?能够,自由开启或者同时开启某些项目?
在这样一系列的需求下,我开发了一款适用于内部框架的 Mock 框架。注意,由于本项目是属于个人项目探索,所以,后期的使用者,可酌情考虑自身需求,决定是否使用。
⚠️ :使用本框架之前,你得了解对应的基础知识
-
lerna
: 一个项目管理工具,可以让你的项目从以前的单体应用变成多体应用集中管理。lerna
的使用原理其实也就是npm link + node_modules
或者yarn workspaces
,lerna 的使用也十分的简单,相信你看了文档,几分钟就可以快速上手构建一个 lerna 应用推荐直接看lerna 文档,或者去搜一下视频课程(PS: 课程不知道有哪些,笔者没看过相关的多体应用开发的视频,这里就没有推荐了哈)。当然,如果你不想通过工具帮你管理,你可以参考 vue3 的源码,可以知道直接用的就是 yarn workspaces,但是这样有个坏处就是,你安装依赖的时候,需要把 yarn workspaces 选项关闭。可能有更好的解决方案吧,这里就不再赘述了。直接上几条最常用的命令。
# 全局安装lerna yarn add global lerna # 初始化项目 lerna init # 创建一个子项目 lerna create <name> # 每个项目安装依赖 lerna bootstrap # 为每个子项目安装某个依赖 lerna add -D ts-node # 为某个项目安装某个依赖 lerna add <packageName> --scope=<projectName> # 执行所有项目的共同脚本(方式1) lerna run start # 执行所有项目的共同脚本(方式2) lerna exec yarn start # 执行某个项目的某个脚本 lerna exec --scope <projectName> -- yarn start # 发布 lerna publish
-
mock
学习怎么使用直接参考文档
-
typescript
学习怎么使用直接参考文档 -
koa
学习怎么使用直接参考文档
- Cli(脚手架)自动创建项目的功能
- 修复路由模块化的问题(采用依赖收集和批量触发的方式可以参考 vue3 的
effect
收集和触发) - 修复打包后
koa
依赖自带的socket问题
(使用 rollup 的commonJs
,打包的时候重写ts
的配置)
- 每个子项目的名称需要是
@qy-mock/
开头的,比如你要创建yuetu
项目,应该使用命令lerna create @qy-mock/yuetu
。这样做的目的是为了方便项目管理,也是属于业界的规范。 - 由于本框架,并没有做什么太多的变体,丝毫不影响你使用。完全可以说,你在
koa
的使用习惯,该怎么写就怎么写,完全不会影响,笔者只是对koa
提供的一些方法进行扩展和抽取而已。至于,本质的东西还是koa
,所以,本框架给使用者很大的自由度,你不喜欢使用本框架提供的方法,你完全可以不用本框架提供的 api。直接在一个koa
的环境编写即可。这样,本框架,可以给你一个干干净净的koa
服务器环境。 - 值得注意的是,为了后期的项目维护,虽然使用者可以不用框架提供的 api,但是,项目架构希望还是能够按照本框架的要求进行。
- 希望使用者记得,本框架原则只是为了提供一个便利的
mock
环境,每个子项目你想要什么技术栈或者方式去编写都可以。再啰嗦一句,请记得,我只是为你的项目环境提供了koa
和mock
环境,其他想要怎么做,就由使用者决定了。
-
确保您已经安装
lerna
,如果没有,请安装yarn global add lerna
或者npm install -g lerna
-
在根目录,安装依赖
yarn install
,然后运行yarn bootstrap
为每个子项目安装依赖 -
在根目录的
.env
文件中配置当前要启动的项目,项目名称使用你创建子项目的名称。比如// .env MOCK_PROJECT = ["@qy-mock/tingshu"];
如果您想要使用命令式创建项目,本项目已经为你准备好了一个脚本命令,您只要运行yarn create:project
就会唤起脚手架,按照操作的提示输入你的期望,最终会帮你生成一个雏形的项目,默认已经帮你安装@qy-mock/core
和@qy-mock/shared
这两个包。然后在.env
文件配置就可以,开启项目了。然后您就可以自由发挥了。
当然,如果你想要自己创建项目也是可以的。您可以运行lerna create <projectName>
进行创建,然后您自己改造好你想要的目录。
本项目已经帮你写好了打包的配置和逻辑。您需要做的就是在根目录的.env
文件中对应的配置项添加上你想要打包的项目就可以了。
rollup 打包后,你可以看到打包后的文件,直接在node
环境执行就可以了,不用再去安装依赖什么的。如果在服务器,推荐使用pm2
来执行,可以维持你的进程状态。
qy-mock
├─ packages 每一个项目的集中管理包
│ ├─ core 核心包(为其他项目提供相关的api)
│ │ ├─ src
│ │ │ ├─ middlewares 内置的中间件
│ │ │ │ ├─ BodyParser.ts post请求体数据的中间件
│ │ │ │ ├─ Cors.ts 跨域处理中间件
│ │ │ │ ├─ index.ts
│ │ │ │ └─ routerLog.ts 路由日志中间件
│ │ │ ├─ customFn.ts 自定义函数相关的api
│ │ │ ├─ event.ts 事件订阅通知的的相关api
│ │ │ ├─ globalData.ts 全局数据共享的api
│ │ │ ├─ http.ts http请求的相关method
│ │ │ ├─ instance.ts 当前应用实例相关的api
│ │ │ ├─ lifecycle.ts 当前应用实例的生命周期
│ │ │ ├─ render.ts 应用挂载
│ │ │ ├─ routeEffect.ts 路由的收集和载入(做到懒加载)
│ │ │ ├─ router.ts 路由处理相关的api
│ │ │ ├─ types.ts 相关的类型定义
│ │ │ └─ utils.ts 一些工具函数
│ │ ├─ index.ts 应用核心包的主入口
│ │ ├─ package.json
│ │ └─ yarn.lock
│ ├─ shared 所有项目共享的工具函数
│ │ ├─ src
│ │ │ ├─ logMsg.ts
│ │ │ └─ utils.ts
│ │ ├─ index.ts
│ │ ├─ package.json
│ │ └─ yarn.lock
├─ scripts
│ ├─ create 脚手架创建项目的核心文件
│ │ ├─ gen.js
│ │ ├─ index.js
│ │ └─ manage.js
│ ├─ helper
│ │ ├─ logMsg.js 日志打印的工具函数
│ │ ├─ pkgManage.js 子项目的管理工具函数
│ │ └─ utils.js
│ ├─ build.js 打包的脚本文件
│ ├─ contants.js 项目打包或者运行的配置文件
│ └─ dev.js 项目在开发环境时的脚本
├─ README.md
├─ lerna.json 项目管理文件
├─ package.json
├─ rollup.config.js 打包配置文件
├─ tsconfig.json
└─ yarn.lock
相关的 api 集中在@qy-mock/core 进行管理,用户只需要使用即可。这样做目的可以让每个项目分工明确,达到隔离性目的
描述:创建一个应用
使用:
import { createApp } from "@qy-mock/core";
const { app, mockApp } = createApp("tingshu");
描述:获取当前 app 应用实例,可以拿到应用保存的相关信息和状态
使用
import { getCurrentAppInstance } from "@qy-mock/core";
const appInstance = getCurrentAppInstance();
console.log(appInstance._port); // 23330
描述:挂载当前应用,接收的参数是端口和挂在之后对应的处理函数
使用
const { mount } = mockApp;
mount("23330");
描述: 卸载应用
使用
const { unmount } = mockApp;
unmount();
描述: 向项目之间提供共享的变量,相当于eolinker
的环境变量设置
使用:
import { provide } from "@qy-mock/core";
provide("foo", { name: "qianyun" });
描述: 向项目之间取到共享的变量,相当于eolinker
的环境变量设置
使用:
import { inject } from "@qy-mock/core";
inject("foo"); //{name: 'qianyun'}
描述: 为项目注册一个全局事件,基于发布订阅模式,通知对应的订阅者执行 handler,注意,在发射事件之前,请确保已经绑定好了该事件的监听函数
使用:
import { emit } from "@qy-mock/core";
emit("add", { count: 10 });
描述: 监听对应某个事件,执行对应的处理函数
使用:
import { on } from "@qy-mock/core";
on("add", (payload) => {
console.log(payload.count); //10
});
router
挂载之前调用,注意,这时候由于router
还没挂载,所以你是拿不到router
的,函数携带的参数为当前的应用实例。
router
挂载之后调用函数携带的参数为当前的应用实例和router
router
即将卸载的时候调用的函数
用于跨域请求的中间件
用于打印出每个接口请求的日志,方便使用者进行观察和调试
统一转换 Post 请求体的数据
内置中间件目前内置这两个,如果使用想要做更多
-
只想要某些内置中间件,使用者可以在
createApp
函数,传入第二个参数,示例const app = createApp("tingshu", { middlewares: ["RouterLogger"] });
-
想要扩展更多的中间件,
使用者想要扩展的话,很简单,一句话,koa 怎么写扩展中间件,你就继续怎么写,你要明白的一件事,本框架只是稍微的对
koa
进行常用的封装,达到项目共享 api 的目的而已。
描述: 使用者可以设置想要保存在项目中的函数,方便随时调用,注意,函数名如果同名,会默认保存最后一次的值。
使用:
import { setCustomFn } from "@qy-mock/core";
setCustomFn("setToken", (ctx) => {
ctx.cookies.set("name", "qianyun");
});
描述: 获取存储在项目中的函数
使用:
import { getCustomFn } from "@qy-mock/core";
const setToken = getCustomFn("setToken");
setToken();
描述: 删除在项目中存储的函数
使用:
import { removeCustomFn } from "@qy-mock/core";
removeCustomFn("setToken");
用户可以自己收集你想要的存储的路由,具体使用你可以看函数的定义。该函数用处适用于熟悉框架本身的人,一般用不到
用户可以自己触发更新已经存储的路由,具体使用你可以看函数的定义。该函数用处适用于熟悉框架本身的人,一般用不到
描述:
如果响应的数据是一个简单的布尔类型或者数字类型,框架内部会自动捕获到,帮你自动返回响应成功
设置基本的响应体,默认的响应体结构如下:
返回true
或者为真的数字,表示成功时:
{
"msg": "success",
"data": 1,
"resultCode": 1
}
返回false
或者为真的数字,表示成功时:
{
"msg": "error",
"data": 0,
"resultCode": 0
}
请注意,以上是框架默认的响应体数据。如果使用者想要改变基本响应体或者对每一个响应体做对应的transform
怎么办?这时候**setBaseResponse
**提供了三个调用的钩子。
-
success
:传入一个对象或者一个处理函数(需要返回一个对象),该对象会重置框架默认的成功响应体数据 -
fail
:传入一个对象或者一个处理函数(需要返回一个对象),该对象会重置框架默认的失败响应体数据 -
customTransformer
:一个对所有响应体进行再次加工的函数。应用场景可在格式化你的数据结构。
使用
import {createApp} from "@qy-mock/core"
const {mockApp} = createApp('tingshu')
mockApp.setBaseResponse({
success: {data:"qianyun",code: 1},
fail: ...,
customTransformer:...
})
框架内部集成了MockJs
,使用者只需要调用就可以正常使用 mock 环境了
示例
import { mock } from "@qy-mock/core";
mock("@cname"); //"杨平"
描述: 根据规则mock
出一个对应长度的数组
示例:
import { mockArray } from "@qy-mock/core";
mockArray("@cname", 3); //["杨平","","天衡"]
框架内部帮你写好了五个请求,分别是Get
,Post
,Delete
,Put
,Options
,使用方式一样的。接下来编写一个示例
import { Get, Post, mock } from "@qy-mock/core";
class TestRouter {
@Get("/mock/:id")
getList(reqData) {
// req框架帮你获取的对应数据
return {
data: {
name: mock("@cname"),
},
resultCode: 1,
};
}
@Post("/mock/:id")
postComment(reqData) {
return {
data: {
name: mock("@cname"),
},
resultCode: 1,
};
}
}
export default TestRouter;
注意: 你会看到每个方法都有个
reqData
内置参数,这是框架帮你捕获你这次请求的相关数据,在里面你可以拿到你的请求信息,作相应的处理。reqData
有什么参数呢?
reqData
拥有的参数为query
,url
,method
,header
,params
,body
。使用者根据自身需要,获取对应的数据即可。
描述: 当你写好了对应的路由模块,需要将它注册到框架中才能使用。理由:虽然内部已经帮你track
过这个路由依赖,但是并没有进行trigger
,如果不想通过registerRouter
注册路由,也可以使用提供的triggerRoute
触发你想要的路由
使用:
import { createApp } from "@qy-mock/core";
const { mockApp } = createApp("tingshu");
mockApp.registerRouter(TestRouter).mount("23331");