前言:在使用Koa2写毕设后端API的时候,每写一个接口,需要分别在
router
、controller
、service
三个文件夹下分别创建文件,在接口很多的情况下,每次在三个文件夹下分别创建对应的文件,是一件很没有效率的事情,那有没有什么比较好的解决方案么?写一个自动生成代码文件的脚本!!!
结合这篇:使用Koa2写接口的一般步骤进行阅读更佳
1、使用到的库
-
ejs:是一套简单的模板语言,帮你利用普通的 JavaScript代码HTML 页面;
-
npm install ejs -D
-
<%
‘脚本’ 标签,用于流程控制,无输出。 -
<%_
删除其前面的空格符 -
<%=
输出数据到模板(输出是转义 HTML 标签) -
<%-
输出非转义的数据到模板 -
<%#
注释标签,不执行、不输出内容 -
<%%
输出字符串 ‘<%’ -
%>
一般结束标签 -
-%>
删除紧随其后的换行符 -
_%>
将结束标签后面的空格符删除
-
-
chalk:它是一个简单实用的控制台格式渲染工具,可以自由定义颜色、背景色以及格式等;
npm install chalk -D
- 效果如下:
- inquirer:它是一个用户与命令行交互的工具;
npm install inquirer -D
- 效果如下:
- change-case:change-case包的主要作用是将字符串在骆驼式格式(camelCase),帕斯卡格式(PascalCase)首字母格式(Title Case),蛇底式格式(snake_case),小写格式(lowercase),大写格式(UPPERCASE),常量格式(CONSTANT_CASE)之间转换。
npm install change-case -D
2、目录结构
2.1、模版代码:
- 在根目录下创建
scripts
文件夹,在其中分别创建controller-template
、router-template
、service-template
三个文件夹,分别是控制器、路由层、服务层(操作数据库的议一些方法)。其中.ejs
文件就是模版代码 controller.ejs
const <%- fileName %>Service = require('../service/<%- fileName %>.service')
class <%- fileName %>Controller {
}
module.exports = new <%- fileName %>Controller();
router.ejs
const Router = require('koa-router');
// 具体的处理逻辑
const {
} = require('../controller/<%- fileName %>.controller');
const <%- fileName %>Router = new Router({
prefix: '/api/<%- fileName %>'
});
<%- fileName %>Router.post('/', );
module.exports = <%- fileName %>Router;
service.ejs
const connection = require('../app/database');
class <%- fileName %>Service {
}
module.exports = new <%- fileName %>Service();
2.2、入口代码
new-file.js
const chalk = require('chalk'); // 终端样式库
const inquirer = require('inquirer'); // 命令行用户交互库
const fs = require('fs');
const path = require('path');
const ejs = require('ejs'); // JavaScript模版引擎库
const changeCase = require('change-case'); // 字符串格式切换库
const fileNameReg = /^[a-z]*$/;
async function getFileName() {
let fileName = process.argv[2]; // 获取命令行输入的参数
if (!fileName) {
const answers = await inquirer.prompt([{
type: 'input',
name: 'fileName',
message: chalk.green('请输入要创建的文件名称(小写字母形式,如test): ')
}]);
if (answers.fileName) {
fileName = answers.fileName;
} else {
fileName = await getFileName();
}
}
if (!fileNameReg.test(fileName)) {
fileName = await getFileName();
}
return fileName;
}
getFileName().then(fileName => {
const pascalFileName = changeCase.pascalCase(fileName); // 开头字母转大写
// 在src/controller目录下创建一个子目录,用于存放file的核心代码
const controllerFile = path.resolve(__dirname, `../src/controller/${fileName}.controller.js`);
// 在src/router目录下创建一个子目录,用于存放file的核心代码
const routerFile = path.resolve(__dirname, `../src/router/${fileName}.router.js`);
// 在src/service目录下创建一个子目录,用于存放file的核心代码
const serviceFile = path.resolve(__dirname, `../src/service/${fileName}.service.js`);
// 检测是否存在该文件
const exists = fs.existsSync(controllerFile) && fs.existsSync(routerFile) && fs.existsSync(serviceFile);
// 拿到模版代码
// 方式一
// const controllerCode = fs.readFileSync(path.resolve(__dirname, "./controller-template/controller.ejs"));
// const routerCode = fs.readFileSync(path.resolve(__dirname, "./router-template/router.ejs"));
// const serviceCode = fs.readFileSync(path.resolve(__dirname, "./service-template/service.ejs"));
// 方式二
const controllerTemplate = path.resolve(__dirname, "./controller-template/controller.ejs");
const routerTemplate = path.resolve(__dirname, "./router-template/router.ejs");
const serviceTemplate = path.resolve(__dirname, "./service-template/service.ejs");
if (exists) {
console.log(`${chalk.red(`文件 ${chalk.bold(fileName)} 代码或文档已存在,请勿重复添加!`)}`);
return;
} else {
/**
* 方式一
* ejs.render(str, data, options);
* => 输出渲染后的 HTML 字符串
*/
// let controllerResult = ejs.render(controllerCode.toString(), {fileName: pascalFileName});
// let routerResult = ejs.render(routerCode.toString(), {fileName: fileName});
// let serviceResult = ejs.render(serviceCode.toString(), {fileName: pascalFileName});
// fs.writeFileSync(controllerFile, controllerResult)
// fs.writeFileSync(routerFile, routerResult)
// fs.writeFileSync(serviceFile, serviceResult)
/**
* 方式二
* ejs.renderFile(filename, data, options, function(err, str){
* // str => 输出渲染后的 HTML 字符串
* });
*/
ejs.renderFile(controllerTemplate, {
fileName: pascalFileName
}, null, function (_, str) {
fs.writeFileSync(controllerFile, str);
});
ejs.renderFile(routerTemplate, {
fileName: fileName
}, null, function (_, str) {
fs.writeFileSync(routerFile, str);
});
ejs.renderFile(serviceTemplate, {
fileName: pascalFileName
}, null, function (_, str) {
fs.writeFileSync(serviceFile, str);
});
}
})
2.3、使用命令生成三个模版
-
在
package.json
文件中的script
内添加"new-file": "node ./scripts/new-file.js"
,在命令终端输入npm run new-file xxx
(xxx是你所创建文件的名字) -
命令有两种使用方法
3、成果展示
3.1、方法一
- 例如我需要创建一个测试的接口,那么我可以这样做:
- 终端输入:
npm run new-file test
,如下所示:
- 最终会创建三个文件,分别是
test.controller.js
、test.router.js
、test.service.js
- 如果再次输入相同的命令,就会提示该文件已经存在
- 红色字体就是
chalk
库的作用,控制器格式渲染 console.log(`${chalk.red(`文件 ${chalk.bold(fileName)} 代码或文档已存在,请勿重复添加!`)}`);
3.2、方案二
- 直接使用命令
npm run new-file
,再输入需要创建的文件名称 - 这个是
inquirer
库的作用:用户与命令行交互的工具(感觉是不是很熟悉,很像使用Vue脚手架创建项目的界面,vue-cli就是使用这个库)
结语
- 以上就是一个目录结构比较简单的自动化生成代码模版方式;
- 目录比较复杂的:比如在组件库中使用,通常封装一个组件,需要在多处文件夹中新建文件,使用脚本自动化生成代码模版的方式,就能大大提高封装组件的效率