<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        使用Node.js寫一個代碼生成器的方法步驟

        來源:懂視網 責編:小采 時間:2020-11-27 21:57:21
        文檔

        使用Node.js寫一個代碼生成器的方法步驟

        使用Node.js寫一個代碼生成器的方法步驟: 背景 第一次接觸代碼生成器用的是動軟代碼生成器,數據庫設計好之后,一鍵生成后端 curd代碼。之后也用過 CodeSmith , T4。目前市面上也有很多優秀的代碼生成器,而且大部分都提供可視化界面操作。 自己寫一個的原因是因為要集成到自己寫的一個小工具中,而
        推薦度:
        導讀使用Node.js寫一個代碼生成器的方法步驟: 背景 第一次接觸代碼生成器用的是動軟代碼生成器,數據庫設計好之后,一鍵生成后端 curd代碼。之后也用過 CodeSmith , T4。目前市面上也有很多優秀的代碼生成器,而且大部分都提供可視化界面操作。 自己寫一個的原因是因為要集成到自己寫的一個小工具中,而

         背景

        第一次接觸代碼生成器用的是動軟代碼生成器,數據庫設計好之后,一鍵生成后端 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));
        

        表示使用 controller.njk 作為模板,nunjucksRenderConfig作為數據(模板內可以獲取到 nunjucksRenderConfig 屬性 data 上的數據)。編譯后進行文件重命名,并保存到指定目錄下。

        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
         }
        }
        
        

        模板中使用最多的就是這個數據,也是生成新代碼需要配置的地方,比如這里配置的是 book ,生成的就是關于 book 的curd 的 mock 服務。要生成別的,修改后執行生成命令即可。

        buildMockData 函數的作用是生成 mock 服務需要的隨機數據,在 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
        }
        

        針對 lazy-mock 的代碼生成的功能就已經完成了,要實現模板的定制直接修改模板文件即可,比如要修改 mock server 服務 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 項目的時候,指定使用相應的模板進行初始化,也就是安裝相應的模板。

        使用 Node.js 寫了一個 CLI 工具 lazy-mock-cli ,已發到 npm ,其功能包含下載指定的遠程模板來初始化新的 lazy-mock 項目。代碼參考( copy )了vue-cli2 。代碼不難,說下某些關鍵點。

        安裝 CLI 工具:

        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 中簡單配置的,原因是我認為一個 mock server 不需要數據庫,lazy-mock 確實如此。

        但是如果寫一個正兒八經的代碼生成器,那肯定是需要根據已經設計好的數據庫表來生成代碼的。那么就需要連接數據庫,讀取數據表的字段信息,比如字段名稱,字段類型,字段描述等。而不同關系型數據庫,讀取表字段信息的 sql 是不一樣的,所以還要寫一堆balabala的判斷。可以使用現成的工具 sequelize-auto , 把它讀取的 model 數據轉成我們需要的格式即可。

        生成前端項目代碼的時候,會遇到這種情況:

        某個目錄結構是這樣的:

        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 就需要在這里加上 "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 的內容,按行進行分割,在數組的最后一個元素之前插入新生成的內容,注意逗號的處理,將新數組內容重新寫入 index.js 中,注意換行。

        打個廣告

        如果你想要快速的創建一個 mock-server,同時還支持數據的持久化,又不需要安裝數據庫,還支持代碼生成器的模板開發,歡迎試試lazy-mock 。

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        使用Node.js寫一個代碼生成器的方法步驟

        使用Node.js寫一個代碼生成器的方法步驟: 背景 第一次接觸代碼生成器用的是動軟代碼生成器,數據庫設計好之后,一鍵生成后端 curd代碼。之后也用過 CodeSmith , T4。目前市面上也有很多優秀的代碼生成器,而且大部分都提供可視化界面操作。 自己寫一個的原因是因為要集成到自己寫的一個小工具中,而
        推薦度:
        標簽: 使用 寫一個 Nodejs
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 精品乱子伦一区二区三区高清免费播放 | 亚洲乱码国产乱码精品精| 中文字幕精品三区无码亚洲| 亚洲人av高清无码| 很黄很污的网站免费| 国产精品二区三区免费播放心| 日本亚洲视频在线| 亚洲国产成人精品无码区二本| 久久免费美女视频| 国产A在亚洲线播放| 无码精品人妻一区二区三区免费看| 永久黄网站色视频免费观看| 亚洲AV色香蕉一区二区| 国产精品手机在线亚洲| 13一14周岁毛片免费| 最新精品亚洲成a人在线观看| 亚洲熟女精品中文字幕| 一级特黄aa毛片免费观看| 久久亚洲AV成人无码电影| 国产三级在线免费观看| 国产成A人亚洲精V品无码性色| 精品四虎免费观看国产高清午夜 | 亚洲国产国产综合一区首页| 国产亚洲精品国产福利在线观看 | 亚洲精品无码高潮喷水在线| 免费女人高潮流视频在线观看| 亚洲成A∨人片天堂网无码| va天堂va亚洲va影视中文字幕 | a级片免费在线播放| 亚洲性日韩精品国产一区二区| 亚洲欧美中文日韩视频| 亚洲精品99久久久久中文字幕 | 蜜臀91精品国产免费观看| 无人视频免费观看免费视频| 日韩激情无码免费毛片| 亚洲av日韩av综合| 成人免费视频88| 亚洲欧美乱色情图片| 亚洲区日韩区无码区| 国产成人精品免费视频大全麻豆 | 国产美女a做受大片免费|