第一次接触代码生成器用的是动软代码生成器,数据库设计好之后,一键生成后端 curd代码。之后也用过 CodeSmith , T4。目前市面上也有很多优秀的代码生成器,而且大部分都提供可视化界面操作。
自己写一个的原因是因为要集成到自己写的一个小工具中,而且使用 Node.js 这种动态脚本语言进行编写更加灵活。
原理
代码生成器的原理就是: 数据 + 模板 => 文件 。
数据 一般为数据库的表字段结构。
模板 的语法与使用的模板引擎有关。
使用模板引擎将 数据 和 模板 进行编译,编译后的内容输出到文件中就得到了一份代码文件。
功能
因为这个代码生成器是要集成到一个小工具lazy-mock 内,这个工具的主要功能是启动一个 mock server 服务,包含curd功能,并且支持数据的持久化,文件变化的时候自动重启服务以最新的代码提供 api mock 服务。
代码生成器的功能就是根据配置的数据和模板,编译后将内容输出到指定的目录文件中。因为添加了新的文件,mock server 服务会自动重启。
还要支持模板的定制与开发,以及使用 CLI 安装模板。
可以开发前端项目的模板,直接将编译后的内容输出到前端项目的相关目录下,webpack 的热更新功能也会起作用。
模板引擎
模板引擎使用的是nunjucks。
lazy-mock 使用的构建工具是 gulp,使用 gulp-nodemon 实现 mock-server 服务的自动重启。所以这里使用 gulp-nunjucks-render 配合 gulp 的构建流程。
代码生成
编写一个 gulp task :
const rename = require('gulp-rename') const nunjucksRender = require('gulp-nunjucks-render') const codeGenerate = require('./templates/generate') const ServerFullPath = require('./package.json').ServerFullPath; //mock -server项目的绝对路径 const FrontendFullPath = require('./package.json').FrontendFullPath; //前端项目的绝对路径 const nunjucksRenderConfig = { path: 'templates/server', envOptions: { tags: { blockStart: '<%', blockEnd: '%>', variableStart: '<$', variableEnd: '$>', commentStart: '<#', commentEnd: '#>' }, }, ext: '.js', //以上是 nunjucks 的配置 ServerFullPath, FrontendFullPath } gulp.task('code', function () { require('events').EventEmitter.defaultMaxListeners = 0 return codeGenerate(gulp, nunjucksRender, rename, nunjucksRenderConfig) });
代码具体结构细节可以打开lazy-mock 进行参照
为了支持模板的开发,以及更灵活的配置,我将代码生成的逻辑全都放在模板目录中。
templates 是存放模板以及数据配置的目录。结构如下:
只生成 lazy-mock 代码的模板中 :
generate.js 的内容如下:
const path = require('path') const CodeGenerateConfig = require('./config').default; const Model = CodeGenerateConfig.model; module.exports = function generate(gulp, nunjucksRender, rename, nunjucksRenderConfig) { nunjucksRenderConfig.data = { model: CodeGenerateConfig.model, config: CodeGenerateConfig.config } const ServerProjectRootPath = nunjucksRenderConfig.ServerFullPath; //server const serverTemplatePath = 'templates/server/' gulp.src(`${serverTemplatePath}controller.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + '.js')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.ControllerRelativePath)); gulp.src(`${serverTemplatePath}service.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + 'Service.js')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.ServiceRelativePath)); gulp.src(`${serverTemplatePath}model.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + 'Model.js')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.ModelRelativePath)); gulp.src(`${serverTemplatePath}db.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + '_db.json')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.DBRelativePath)); return gulp.src(`${serverTemplatePath}route.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + 'Route.js')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.RouteRelativePath)); }
类似:
gulp.src(`${serverTemplatePath}controller.njk`) .pipe(nunjucksRender(nunjucksRenderConfig)) .pipe(rename(Model.name + '.js')) .pipe(gulp.dest(ServerProjectRootPath + CodeGenerateConfig.config.ControllerRelativePath));