前端工程化:使用 shelljs 生成 yapi 接口文件
之前的文章介绍了使用 yapi-to-typescript (下文简称 ytt)生成接口类型定义文件,方便我们直接使用接口的请求和响应类型,能减少很多写接口类型的时间。
使用 yapi-to-typescript 生成接口响应数据的 TS 类型声明
既然能生成接口类型定义文件,那也就可以生成接口请求代码文件咯,可是网上找了下没找到有相关的工具库,想必是每个公司的项目接口请求代码风格差异较大,要开发一个可以灵活配置生成对应代码的库也略显麻烦,比如这篇文章就是教大家如何写出生成请求代码的文件,而我也没有写一个这样的库。
前置条件
首先需要了解两个工具库,一个是 ytt,一个是 shelljs。因为 ytt 已经帮我们请求 yapi 拿到了接口数据,我们就没有必要再去获取一次了。
shelljs 是一个具有 shell 功能的工具库,很强大,这里主要用来操作文件系统。
开搞
第一步:存储接口数据
ytt 中提供了钩子函数,在类型文件生成完成后会调用,但是这个函数中我们拿不到 yapi 上的接口,所以需要在 ytt 的 outputFilePath 中手动保存下来。
首先,我们定义两个变量和一个方法:
// 所有接口信息
let interfaceInfos: MyInterface[] = [];
// 所有生成的文件名。方便每次生成的时候先删除
let filesName = new Set<string>();
// 存储所有接口信息。在类型定义文件生成成功后的 success 钩子函数中遍历生成接口调用文件。
const saveInterface = (interfaceInfo: Interface, name: string) => {
filesName.add(name);
interfaceInfo.fileName = name; // 接口数据中增加 fileName 字段,告知这个接口属于哪个文件
interfaceInfos.push(interfaceInfo as MyInterface);
};
在 outputFilePath 将所有接口保存下来:
outputFilePath: (interfaceInfo: Interface) => {
// 接口文档中分类有中文,使用拼音库转成英文拼音
let nameArr: string[] = pinyin(interfaceInfo._category.name, {
toneType: 'none',
nonZh: 'consecutive',
type: 'array',
});
let name: string = camelCase(nameArr.join('-'));
// 这个函数就是保存所有接口信息的函数
saveInterface(interfaceInfo, name);
return `src/types/api/${name}.ts`;
},
在 ytt 的 success 钩子函数中我们就可以编写生成请求代码的代码了:
// 这里是 ytt 完成的钩子函数
{
success() {
shelljs.echo('--------------------接口请求代码生成中...');
// 先删除文件,每次重新生成
filesName.forEach((fileName) => {
shelljs.rm('-f', getFilePath(fileName));
});
// 遍历接口信息生成接口代码
interfaceInfos.forEach((item) => {
generatorRequest(item);
});
shelljs.echo('--------------------接口请求代码生成完成');
},
}
生成接口代码
生成接口代码有几点要注意:
- 接口文件只能生成一次,在第一次生成的时候往里面写入公共代码,如 import 语句。
- 在遍历每个接口数据的时候,可以通过上面往接口数据中添加的 fileName 字段知道这个接口属于哪个文件。
- 对 rest 风格地址的处理。
入口函数:
// 生成接口请求代码
const generatorRequest = (data: MyInterface): void => {
const filePath = getFilePath(data.fileName);
writeCommon(data.fileName, filePath);
const fnName = getFnName(data);
const [reqType, resType] = getReqResType(fnName);
const str = `
export const ${fnName} = (${getRest(data)}data?: Types.${reqType}, options?: Options) => {
return ${getMethod(data)}<Types.${resType}>(
\`${getReqPath(data)}\`,
data,
options
);
};
`;
shelljs.ShellString(str).toEnd(filePath);
};
封装 getFilePath 获取操作的文件路径,我把所有文件写在 src/services 中。
type MyInterface = Interface & { fileName: string };
// 生成文件目录路径
const basePath = './src/services';
// 获取编辑的文件的相对路径
export const getFilePath = (fileName: string): string => {
return `${basePath}/${fileName}.ts`;
};
写入公共内容:
// 写入公共内容,如 import 的依赖,只写一次
const writeCommon = (fileName: string, filePath: string): void => {
// 使用 shelljs.find 方法判断文件是否存在,从而一些公共内容在首次创建时写入
const res = shelljs.find(filePath);
// 0 是找到了,1 是没找到
if (res.code === 1) {
// 写入 import 代码
shelljs
.ShellString(
`
import * as Types from '@/types/api/${fileName}';
import request, { Options } from '@/utils/request';
`
)
.to(filePath);
}
};
写入公共代码后,generatorRequest 中以下的代码就是生成每条请求语句,追加到对应的文件中:
const fnName = getFnName(data);
const [reqType, resType] = getReqResType(fnName);
const str = `
export const ${fnName} = (${getRest(data)}data?: Types.${reqType}, options?: Options) => {
return ${getMethod(data)}<Types.${resType}>(
\`${getReqPath(data)}\`,
data,
options
);
};
`;
shelljs.ShellString(str).toEnd(filePath);
通过请求地址生成请求的函数名:
// 生成函数名
const getFnName = (data: Interface): string => {
const { path } = data;
return camelCase(path);
};
由于 ytt 生成的类型名称也是通过路径生成的,所以我们将路径生成的函数名再稍加处理就可以生成请求和响应的类型名称了
// 生成请求和响应类型的字符串。路径生成的函数名首字符大写,然后加 Request 或 Response
const getReqResType = (fnName: string): string[] => {
const name = fnName.charAt(0).toUpperCase() + fnName.slice(1);
return [`${name}Request`, `${name}Response`];
};
最后一点要注意的是,由于项目中一些请求路径是 rest 风格的,需要对其进行处理。如 service/getUserInfo/:uid 这样的请求地址。
这里我通过正则匹配替换,将 :uid 替换成 ${rest.uid} 这样的字符串。然后函数中添加一个参数 rest。当然,也可以直接生成 ${data.uid},只是这样又得去给请求参数的类型添加联合类型了。
// 获取请求地址
const getReqPath = (data: Interface): string => {
let path = data.path;
if (path.startsWith('/')) {
path = path.substring(1);
}
path = convertReqPath(path);
return path;
};
// 转换请求地址中的 :xxx 为 ${rest.xxx}
const convertReqPath = (path: string): string => {
return path.replace(/:\w+/, (str) => {
return `\${rest.${str.slice(1)}}`;
});
};
最后在写一个函数生成请求函数中的 rest 参数:
// 请求 url 中有 :xxx 的时候,添加第一个参数 rest
const getRest = (data: Interface): string => {
if (/:\w+/.test(data.path)) {
return 'rest: Record<string, string>, ';
} else {
return '';
}
};
以上这些函数就用在了这个模板字符串中:
const str = `
export const ${fnName} = (${getRest(data)}data?: Types.${reqType}, options?: Options) => {
return ${getMethod(data)}<Types.${resType}>(
\`${getReqPath(data)}\`,
data,
options
);
};
`;
大功告成,运行 ytt 生成接口类型文件的同时,也生成了这样的请求文件:

前端工程化:使用 shelljs 生成 yapi 接口文件的更多相关文章
- 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML(转)
1.什么是工程化开发 软件工程的工程化开发概念由来已久,但对于前端开发来说,我们没有像VS或者eclipse这样量身打造的IDE,因为在大多数人眼中,前端代码无需编译,因此只要一个浏览器来运行调试就行 ...
- 基于gulp编写的一个简单实用的前端开发环境好了,安装完Gulp后,接下来是你大展身手的时候了,在你自己的电脑上面随便哪个地方建一个目录,打开命令行,然后进入创建好的目录里面,开始撸代码,关于生成的json文件请点击这里https://docs.npmjs.com/files/package.json,打开的速度看你的网速了注意:以下是为了演示 ,我建的一个目录结构,你自己可以根据项目需求自己建目
自从Node.js出现以来,基于其的前端开发的工具框架也越来越多了,从Grunt到Gulp再到现在很火的WebPack,所有的这些新的东西的出现都极大的解放了我们在前端领域的开发,作为一个在前端领域里 ...
- vuex前端工程化之动态导入文件--require.context( )
随着项目的复杂,文件结构越来越多,Store中modules中的文件也越来越多,如果一个一个加载是不是很麻烦呢? 先看一个项目中store项目结构: 过去都是通过import分别引入文件: 1 imp ...
- 前端工程化(二)---webpack配置
导航 前端工程化(一)---工程基础目录搭建 前端工程化(二)---webpack配置 前端工程化(三)---Vue的开发模式 前端工程化(四)---helloWord 继续上一遍的配置,本节主要记录 ...
- 页面仔初窥"前端工程化"
今天看了几篇前端界的一位大牛--张云龙的文章,其中一篇在自己的理解范围内看得懂一些,有所收获,说的是前端工程化的事,看完算是对前端工程形成了一个模糊的概念. 现在我所接触到的前端开发,还是张云龙大神所 ...
- 公司内部技术分享之Vue.js和前端工程化
今天主要的核心话题是Vue.js和前端工程化.我将结合我这两年多的工作学习经历来谈谈这个,主要侧重点是前端工程化,Vue.js侧重点相对前端工程化,比重不是特别大. Vue.js Vue.js和Rea ...
- 10分钟学会前端工程化(webpack4.0)
一.概要 1.1.前端工程化 随着前端的不断发展与壮大,前端变得越来越复杂,组件化.模块化.工程化.自动化成了前端发展中不可或缺的一部分,具体到前端工程化,面临的问题是如何提高编码->测试-&g ...
- web前端工程化/构建自动化
前端工程化 前端工程化的概念在近些年来逐渐成为主流构建大型web应用不可或缺的一部分,在此我通过以下这三方面总结一下自己的理解. 为什么需要前端工程化. 前端工程化的演化. 怎么实现前端工程化. 为什 ...
- chrome插件: yapi 接口TypeScript代码生成器
前言 2020-09-12 天气晴,蓝天白云,微风,甚好. 前端Jser一枚,在公司的电脑前,浏览器打开着yapi的接口文档,那密密麻麻的接口数据,要一个一个的去敲打成为TypeScript的inte ...
随机推荐
- Nginx网络压缩 CSS压缩 图片压缩 JSON压缩
一.序言 使用Nginx作为web应用服务时,会代理如下常见文件:js.css.JSON.图片等,本文提供基于Nginx内置的压缩技术,提供网络请求响应速度的解决方案. 1.网络压缩原理 网络压缩的原 ...
- Linux中Swap与Memory内存简单介绍 (转)
https://blog.csdn.net/zwan0518/article/details/12059213 一.背景介绍 对于Linux来说,其在服务器市场的使用已经占据了绝对的霸主地位,不可动摇 ...
- 通过Xib加载控制器的View
1.创建窗口self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];2.设置窗口根控制器2.1从XIB当 ...
- C++网络编程卷1、卷2概述
转载请注明来源:https://www.cnblogs.com/hookjc/ 一: C++NPv1主要涉及到的类: 1.1. Logging_Server 是个抽象类,用来定义接口类型,此类不能 ...
- 日期类 Date
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...
- 海康PTZ云台摄像头调试之直接控制云台(C#)
众所周知,海康的摄像头sdk较为完善,但是对于新手来说还是有点麻烦. 今天写一篇随笔给大家展示下怎么控制海康摄像头的云台(前提是有ptz云台设备) 1.sdk准备 本文基于C#的frame来开发一个p ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「SHOI2016」「洛谷 P4336」黑暗前的幻想乡
\(\mathcal{Description}\) link. 有一个 \(n\) 个结点的无向图,给定 \(n-1\) 组边集,求从每组边集选出恰一条边最终构成树的方案树.对 \(10^9+ ...
- 利用 pip download 打包软件来提供离线安装
文章目录 1.通过 pip download 下载安装包 2.利用 pip install --no-index 离线安装 1.通过 pip download 下载安装包 linux-oz6w:~ # ...
- 掌握这20个JS技巧,做一个不加班的前端人
摘要:JavaScript 真的是一门很棒的语言,值得学习和使用.对于给定的问题,可以有不止一种方法来达到相同的解决方案.在本文中,我们将只讨论最快的. 本文分享自华为云社区<提高代码效率的 2 ...