задний план
Первым контактом с генератором кода является генератор динамического мягкого кода.После того, как база данных разработана, код исходного кода генерируется одним щелчком мыши. Я также использовал CodeSmith, T4 позже. На рынке также есть много отличных генераторов кода, и большинство из них обеспечивают работу с визуальным интерфейсом.
Причина его написания в том, что его нужно интегрировать в небольшой инструмент, написанный мной, и более гибко использовать Node.js, динамический язык сценариев.
принцип
Принцип генератора кода:数据 + 模板 => 文件
.
数据
Как правило, это структура поля таблицы базы данных.
模板
Синтаксис зависит от используемого шаблонизатора.
Используйте механизм шаблонов для数据
и模板
Скомпилируйте и выведите скомпилированное содержимое в файл, чтобы получить файл кода.
Функции
Потому что этот генератор кода должен быть интегрирован в гаджетlazy-mockВнутри основной функцией этого инструмента является запуск службы имитации сервера, включая функцию curd, и поддержка сохранения данных, автоматический перезапуск службы при изменении файла и предоставление службы имитации API с последним кодом.
Функция генератора кода заключается в выводе содержимого в указанный файл каталога после компиляции в соответствии с настроенными данными и шаблоном. По мере добавления новых файлов служба фиктивного сервера автоматически перезапускается.
Он также поддерживает настройку и разработку шаблонов, а также установку шаблонов с помощью интерфейса командной строки.
Вы можете разработать шаблон внешнего проекта и напрямую вывести скомпилированный контент в соответствующий каталог внешнего проекта, а также будет работать функция горячего обновления веб-пакета.
шаблонизатор
Механизм шаблонов используетnunjucks.
lazy-mockИспользуемый инструмент сборки — gulp, а gulp-nodemon используется для автоматического перезапуска службы mock-server. Итак, здесь мы используем gulp-nunjucks-render для взаимодействия с процессом сборки gulp.
генерация кода
Напишите глотковую задачу:
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
Это каталог, в котором хранятся шаблоны и конфигурации данных. Структура выглядит следующим образом:
В шаблоне, который генерирует только ленивый макетный код:
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));
Указывает, что в качестве шаблона используется controller.njk, а в качестве данных используется nunjucksRenderConfig (данные о данных атрибута nunjucksRenderConfig можно получить в шаблоне). После компиляции переименуйте файл и сохраните его в указанную директорию.
model.js
Содержание следующее:
var shortid = require('shortid')
var Mock = require('mockjs')
var Random = Mock.Random
//必须包含字段id
export default {
name: "book",
Name: "Book",
properties: [
{
key: "id",
title: "id"
},
{
key: "name",
title: "书名"
},
{
key: "author",
title: "作者"
},
{
key: "press",
title: "出版社"
}
],
buildMockData: function () {//不需要生成设为false
let data = []
for (let i = 0; i < 100; i++) {
data.push({
id: shortid.generate(),
name: Random.cword(5, 7),
author: Random.cname(),
press: Random.cword(5, 7)
})
}
return data
}
}
Эти данные являются наиболее часто используемыми данными в шаблоне, а также это место, где необходимо настроить новый код. Например, здесь настраивается книга, а сгенерированный мок-сервис — о твороге книги. Чтобы сгенерировать что-то еще, выполните команду generate после модификации.
Роль функции buildMockData заключается в генерации случайных данных, необходимых фиктивному сервису, которые будут использоваться в шаблоне db.njk:
{
"<$ model.name $>":<% if model.buildMockData %><$ model.buildMockData()|dump|safe $><% else %>[]<% endif %>
}
Это также то, как Nunjucks выполняет функции в шаблонах
config.js
Содержание следующее:
export default {
//server
RouteRelativePath: '/src/routes/',
ControllerRelativePath: '/src/controllers/',
ServiceRelativePath: '/src/services/',
ModelRelativePath: '/src/models/',
DBRelativePath: '/src/db/'
}
Настройте место, где соответствующий шаблон будет сохранен после компиляции.
config/index.js
Содержание следующее:
import model from './model';
import config from './config';
export default {
model,
config
}
Функция генерации кода для ленивого макета завершена. Чтобы реализовать настройку шаблона, вы можете напрямую изменить файл шаблона. Например, чтобы изменить определение интерфейса API-интерфейса службы фиктивного сервера, непосредственно измените файл route.njk. :
import KoaRouter from 'koa-router'
import controllers from '../controllers/index.js'
import PermissionCheck from '../middleware/PermissionCheck'
const router = new KoaRouter()
router
.get('/<$ model.name $>/paged', controllers.<$model.name $>.get<$ model.Name $>PagedList)
.get('/<$ model.name $>/:id', controllers.<$ model.name $>.get<$ model.Name $>)
.del('/<$ model.name $>/del', controllers.<$ model.name $>.del<$ model.Name $>)
.del('/<$ model.name $>/batchdel', controllers.<$ model.name $>.del<$ model.Name $>s)
.post('/<$ model.name $>/save', controllers.<$ model.name $>.save<$ model.Name $>)
module.exports = router
Разработка и установка шаблона
Разные проекты имеют разную структуру кода, и будет очень проблематично каждый раз напрямую изменять файл шаблона.
Необходимо предусмотреть такую функцию: разработать набор независимых шаблонов для разных проектов и поддерживать установку шаблонов.
Соответствующая логика генерации кода находится в файлах каталога шаблонов.Правил разработки шаблонов нет, главное, чтобы имя каталога было гарантировано.templates
,generate.js
экспорт вgenerate
функция.
Принцип установки шаблона заключается в перезаписывании всех файлов в каталоге шаблона. Однако конкретная установка делится на локальную установку и онлайн-установку.
Как было сказано ранее, этот генератор кода интегрирован вlazy-mock, мой подход заключается в инициализации новогоlazy-mockПри создании проекта укажите использовать соответствующий шаблон для инициализации, то есть установите соответствующий шаблон.
Написал инструмент CLI, используя Node.jslazy-mock-cli, размещенный в npm , функции которого включают в себя загрузку указанного удаленного шаблона для инициализации нового проекта отложенной имитации. Ссылка на код ( копия )vue-cli2. Код не сложный, поговорим о некоторых ключевых моментах.
Установите инструменты командной строки:
npm install lazy-mock -g
Инициализируйте проект с помощью шаблона:
lazy-mock init d2-admin-pm my-project
d2-admin-pmэто я для одногоИнтерфейсный проектШаблон написан.
init
Вызванная командаlazy-mock-init.jsЛогика в:
#!/usr/bin/env node
const download = require('download-git-repo')
const program = require('commander')
const ora = require('ora')
const exists = require('fs').existsSync
const rm = require('rimraf').sync
const path = require('path')
const chalk = require('chalk')
const inquirer = require('inquirer')
const home = require('user-home')
const fse = require('fs-extra')
const tildify = require('tildify')
const cliSpinners = require('cli-spinners');
const logger = require('../lib/logger')
const localPath = require('../lib/local-path')
const isLocalPath = localPath.isLocalPath
const getTemplatePath = localPath.getTemplatePath
program.usage('<template-name> [project-name]')
.option('-c, --clone', 'use git clone')
.option('--offline', 'use cached template')
program.on('--help', () => {
console.log(' Examples:')
console.log()
console.log(chalk.gray(' # create a new project with an official template'))
console.log(' $ lazy-mock init d2-admin-pm my-project')
console.log()
console.log(chalk.gray(' # create a new project straight from a github template'))
console.log(' $ vue init username/repo my-project')
console.log()
})
function help() {
program.parse(process.argv)
if (program.args.length < 1) return program.help()
}
help()
//模板
let template = program.args[0]
//判断是否使用官方模板
const hasSlash = template.indexOf('/') > -1
//项目名称
const rawName = program.args[1]
//在当前文件下创建
const inPlace = !rawName || rawName === '.'
//项目名称
const name = inPlace ? path.relative('../', process.cwd()) : rawName
//创建项目完整目标位置
const to = path.resolve(rawName || '.')
const clone = program.clone || false
//缓存位置
const serverTmp = path.join(home, '.lazy-mock', 'sever')
const tmp = path.join(home, '.lazy-mock', 'templates', template.replace(/[\/:]/g, '-'))
if (program.offline) {
console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
template = tmp
}
//判断是否当前目录下初始化或者覆盖已有目录
if (inPlace || exists(to)) {
inquirer.prompt([{
type: 'confirm',
message: inPlace
? 'Generate project in current directory?'
: 'Target directory exists. Continue?',
name: 'ok'
}]).then(answers => {
if (answers.ok) {
run()
}
}).catch(logger.fatal)
} else {
run()
}
function run() {
//使用本地缓存
if (isLocalPath(template)) {
const templatePath = getTemplatePath(template)
if (exists(templatePath)) {
generate(name, templatePath, to, err => {
if (err) logger.fatal(err)
console.log()
logger.success('Generated "%s"', name)
})
} else {
logger.fatal('Local template "%s" not found.', template)
}
} else {
if (!hasSlash) {
//使用官方模板
const officialTemplate = 'lazy-mock-templates/' + template
downloadAndGenerate(officialTemplate)
} else {
downloadAndGenerate(template)
}
}
}
function downloadAndGenerate(template) {
downloadServer(() => {
downloadTemplate(template)
})
}
function downloadServer(done) {
const spinner = ora('downloading server')
spinner.spinner = cliSpinners.bouncingBall
spinner.start()
if (exists(serverTmp)) rm(serverTmp)
download('wjkang/lazy-mock', serverTmp, { clone }, err => {
spinner.stop()
if (err) logger.fatal('Failed to download server ' + template + ': ' + err.message.trim())
done()
})
}
function downloadTemplate(template) {
const spinner = ora('downloading template')
spinner.spinner = cliSpinners.bouncingBall
spinner.start()
if (exists(tmp)) rm(tmp)
download(template, tmp, { clone }, err => {
spinner.stop()
if (err) logger.fatal('Failed to download template ' + template + ': ' + err.message.trim())
generate(name, tmp, to, err => {
if (err) logger.fatal(err)
console.log()
logger.success('Generated "%s"', name)
})
})
}
function generate(name, src, dest, done) {
try {
fse.removeSync(path.join(serverTmp, 'templates'))
const packageObj = fse.readJsonSync(path.join(serverTmp, 'package.json'))
packageObj.name = name
packageObj.author = ""
packageObj.description = ""
packageObj.ServerFullPath = path.join(dest)
packageObj.FrontendFullPath = path.join(dest, "front-page")
fse.writeJsonSync(path.join(serverTmp, 'package.json'), packageObj, { spaces: 2 })
fse.copySync(serverTmp, dest)
fse.copySync(path.join(src, 'templates'), path.join(dest, 'templates'))
} catch (err) {
done(err)
return
}
done()
}
Решается, следует ли использовать локально кэшированный шаблон или получить последний шаблон, а также выбрать ли онлайн-шаблон из официального репозитория или из другого репозитория.
некоторые мелкие проблемы
В настоящее время соответствующие данные, генерируемые кодом, поступают не из базы данных, а вmodel.js
Причина в том, что я не думаю, что фиктивному серверу нужна база данных, а ленивому макету — нужна.
Но если вы пишете серьезный генератор кода, вы должны генерировать код на основе уже разработанной таблицы базы данных. Затем вам нужно подключиться к базе данных и прочитать информацию о поле таблицы данных, такую как имя поля, тип поля, описание поля и т. д. Для разных реляционных БД sql для чтения информации о полях таблицы разный, поэтому приходится писать кучу балабалных суждений. Можно использовать готовые инструментыsequelize-auto, просто преобразуйте данные модели, которые он считывает, в нужный нам формат.
При генерации кода внешнего интерфейса вы столкнетесь с такой ситуацией:
Определенная структура каталогов выглядит так:
index.js
Содержание:
import layoutHeaderAside from '@/layout/header-aside'
export default {
"layoutHeaderAside": layoutHeaderAside,
"menu": () => import(/* webpackChunkName: "menu" */'@/pages/sys/menu'),
"route": () => import(/* webpackChunkName: "route" */'@/pages/sys/route'),
"role": () => import(/* webpackChunkName: "role" */'@/pages/sys/role'),
"user": () => import(/* webpackChunkName: "user" */'@/pages/sys/user'),
"interface": () => import(/* webpackChunkName: "interface" */'@/pages/sys/interface')
}
Если вы добавляете книгу, вам нужно добавить ее здесь"book": () => import(/* webpackChunkName: "book" */'@/pages/sys/book')
Эта строка содержимого также может быть создана путем настройки шаблона.Например, содержимое шаблона:
"<$ model.name $>": () => import(/* webpackChunkName: "<$ model.name $>" */'@/pages<$ model.module $><$ model.name $>')
Но как добавить сгенерированный контент вindex.js
в?
Первый способ: скопировать и вставить
Второй метод:
Шаблон для этой частиrouterMapComponent.njk:
export default {
"<$ model.name $>": () => import(/* webpackChunkName: "<$ model.name $>" */'@/pages<$ model.module $><$ model.name $>')
}
Скомпилированный файл сохраняется в каталоге routerMapComponents, например book.js.
Измените index.js:
const files = require.context('./', true, /\.js$/);
import layoutHeaderAside from '@/layout/header-aside'
let componentMaps = {
"layoutHeaderAside": layoutHeaderAside,
"menu": () => import(/* webpackChunkName: "menu" */'@/pages/sys/menu'),
"route": () => import(/* webpackChunkName: "route" */'@/pages/sys/route'),
"role": () => import(/* webpackChunkName: "role" */'@/pages/sys/role'),
"user": () => import(/* webpackChunkName: "user" */'@/pages/sys/user'),
"interface": () => import(/* webpackChunkName: "interface" */'@/pages/sys/interface'),
}
files.keys().forEach((key) => {
if (key === './index.js') return
Object.assign(componentMaps, files(key).default)
})
export default componentMaps
использовать require.context
я тоже пользуюсь этим методом
Третий метод:
При разработке шаблона сделайте специальную обработку, прочитайте содержимое исходного index.js, разделите его построчно, вставьте вновь сгенерированный контент перед последним элементом массива, обратите внимание на обработку запятых и перепишите новый массив содержимое в индекс .js, обратите внимание на разрывы строк.
Рекламировать
Если вы хотите быстро создать Mock-Server, а также поддерживать постоянство данных, вам не нужно устанавливать базу данных, а также поддерживать разработку шаблона генератора кода, добро пожаловать попробоватьlazy-mock.