新生代小鲜肉之代码生成器

简介: 如果你能耐心看完这个对话,也许你会想动动手配置一个代码生成器,这样你的撸码生活可能从此就会有所变化,变得愈发轻松。

88356c677e048fd36180ac12c6b97de7.png

如果你能耐心看完这个对话,

也许你会想动动手配置一个代码生成器,

这样你的撸码生活可能从此就会有所变化,变得愈发轻松。

从一个脚手架说起


丹尼尔: 最近要搞个代码生成器,能够快速生成项目代码那种,蛋兄有什么推荐?


蛋先生: 过往一直使用 yeoman,快超 10k star 的开源项目。但是,今天要推荐给你的,并非 yeoman,而是一个新生代的小鲜肉 ncgen,它可能显得更加的平易近人。


丹尼尔: 我最最喜欢简单的了,这个咋用呢?


蛋先生: 老规矩,你说你的需求,我试着一一解答


首先,你需要一个项目模板


丹尼尔: 我有一个项目模板(比如:vue3-ncgen-demo),我希望新建的项目都来自于这个项目模板,这样我只需专心维护好这个项目模板即可。


蛋先生: OK,这就是项目脚手架的功能了。我们来看下 ncgen 是如何处理的。


第一步 安装 ncgen


$ npm i ncgen -g # yarn global add ncgen


第二步 生成配置文件 ncgen-config.js,该配置文件描述了代码生成器的逻辑


$ ncgen genConf


第三步 修改 ncgen-config.js 中的 main.tmplSource 为项目模板的地址。


export default {  main: {    tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",  },};


运行一下试试:


$ ncgen ncgen-config.js


623c22a1cebb7b51dd53b57df4387f2d.gif


简单复制还不行,小修小改是常态


丹尼尔: 哎呦不错哦。不过目前生成的项目跟项目模板是一模一样的,但它总会有属于自己的跟项目模板不一些的信息,比如项目名称,作者姓名等。这些我可不希望每次生成完项目还要手工修改哦。


蛋先生: OK,要求非常合理。由于这些信息是创建项目的人才能提供,所以我们需要通过一些问题来收集这些信息,然后就可以根据这些信息对生成的项目作一些修改。我们修改下 ncgen-config.js 中的 main.promptmain.updateFiles


示例说明:

对生成项目中的 package.json 文件进行字符串替换,规则如下:

vue3-ncgen-demo 字符串替换成用户录入的项目名称

Daniel.xiao 字符串替换成用户录入的作者名称


export default {  main: {    prompt: [      {        type: "input",        name: "author",        message: "What is the author's name",      },    ],
    ...
    updateFiles: {      "package.json": function (content, options) {        const answers = this.$answers        return api.replace(content, {          "vue3-ncgen-demo": answers.projectNameObj.kebabCase,          "Daniel.xiao": answers.author,        });      },    },  }};


66efd52f1602c54be71552e8311eb7ac.gif


丹尼尔: 咦,我注意到这里并没使用模板引擎,而是直接使用字符串替换


蛋先生: 是的,这个设计有很大的意义。用了模板引擎来替换文件,可能会导致项目模板自身无法正常运行,因为模板引擎需要占位符,而占位符可能会导致代码解析错误


丹尼尔: 也是,这样一来项目模板就是一个普通的项目而已,也不用特意去做一些模板占位符的改造


多余的文件昨办?请删除


丹尼尔: 那我继续。我的项目模板里面放了一些模板目录和文件(比如模块模板目录,组件模板文件),但我不想在生成的项目里面看到这些模板的东西。


蛋先生: OK,没问题,就是需要删除指定的文件和目录。我们修改下 ncgen-config.js 中的 main.removeFiles


示例说明:

删除生成项目中的 ncgen-config.jssrc/components/base/Template.vue 文件


export default {  main: {    removeFiles: ["ncgen-config.js", "src/components/base/Template.vue"],  },};


一条龙服务:自动安装依赖


丹尼尔: 我刚刚有注意到,上面的例子在运行的时候会自动安装依赖,应该是用 npm 安装的吧,这个能支持 yarn 吗?如果我是非 NodeJS 项目,比如 Python,Go 等,也能做到吗?


蛋先生: 嗯,没错,生成的 ncgen-config.js 默认是使用 npm i 来安装依赖,看下边的示例。如果你想换成 yarn,只需把 command 改成 yarn install。而如果是 Python,Go 等其它语言,也只需将 command 改成对应的依赖安装命令即可


export default {  main: {    installDependencies: {      skip: false,      tips: "Dependencies are being installed, it may take a few minutes",      command: "npm i",    },  },};


最后,给点友好的提示


丹尼尔: 太棒了,项目脚手架就这么配几下就完成了,我想最后需要来个友好的欢迎和漂亮的 ending


蛋先生: 如你所愿,简单修改下 main.welcomemain.complete 即可


export default {  main: {    welcome: "Welcome to use (Vue 3 + TypeScript + Vite) project generator",        ...        complete: "Congratulations, the operation is successful",  },};


高频使用的并非脚手架


丹尼尔: 脚手架是搞定了,但它只在新建项目时才使用,高频操作还是部分代码的增加,比如加一个功能模块,加一个组件,加一个 API 之类的


蛋先生: 我理解你的意思。老规矩,你问我答


代码模板存在于项目模板中


丹尼尔: 我现在要在一个项目中新加一个组件,我并不希望复制一个已有组件,然后进行各种修改删除代码的操作。事实上项目模板中有组件的代码模板


蛋先生: OK。我们先在 ncgen-config.js 中增加一个叫 add-component 的子命令。


示例说明(假设 category 和 name 的值分别为 'busi' 和 'demo'):

description 用于描述子命令的功能。

api.listDirs 这个 API,在让用户选择将代码插入到哪个位置时非常有用。

addFilesTo 的配置会将项目模板中的 src/components/base/Template.vue 插入到项目中的 src/components/busi/Demo.vue 文件。


export default {  sub: {    "add-component": {      description: "Add vue component",            prompt: [        {          type: "list",          choices: function () {            return api.listDirs("src/components/");          },          name: "category",          message: "Please select the category",        },        {          type: "input",          name: "name",          message: "What is the component name",          validate(input) {            if (!input) return "The component name is required";            return true;          },        },      ],            tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",            addFilesTo: function () {        const answers = this.$answers;        return {          "src/components/base/Template.vue": `src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`,        };      },    },  },};


d52e16a60f96d20ded9c3c0b064c81f8.gif


代码模板不存在于项目模板中


丹尼尔: 漂亮。不过对于已经存在的项目,这些项目并非来自项目模板,而我也想加一些子命令来为项目生成部分代码,昨整?


蛋先生: 子命令支持两种添加文件的方式,一种就是上面提到的来自于项目模板的代码模板,还有一种就是你动态创建。两者可同时使用。以下示例演示如何动态创建代码文件


示例说明(假设 category 和 name 的值分别为 'busi' 和 'demo'):

addFiles 的配置会在项目中创建 src/components/busi/Demo.md 文件,这个文件的内容为 # Demo


export default {  sub: {    "add-component": {      addFiles: function () {        const answers = this.$answers;        return {          [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.md`]: function () {            return `# ${answers.nameObj.upperFirstCamelCase}`;          },        };      },    },  },};


墙裂推荐的替换技巧


丹尼尔: 接着,又是进行一些文件内容的修改(比如增加了页面,会自动修改路由规则文件为页面注册路由),跟主命令一样的操作是吧。


蛋先生: 悟性很高嘛。这里推荐一个小技巧,就是在需要插入片段代码的地方加入一些标识注释,如 src/App.vue 代码所示:


<template>  <img alt="Vue logo" src="./assets/logo.png" />  <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />  <!-- Don't touch me - place component --></template>
<script lang="ts">import { defineComponent } from 'vue'import HelloWorld from './components/busi/HelloWorld.vue'// <!-- Don't touch me - import component -->export default defineComponent({  name: 'App',  components: {    HelloWorld,    // <!-- Don't touch me - register component -->  }})</script>


再配合 api.insertBefore 这个 API 在文件的指定匹配位置前插入指定的内容


export default {  sub: {    updateFiles: function () {      const answers = this.$answers;      return {        "src/App.vue": function (content, options) {          return api.insertBefore(content, {            "// <!-- Don't touch me - import component -->": `import ${answers.nameObj.upperFirstCamelCase} from './components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue'`,            "// <!-- Don't touch me - register component -->": `${answers.nameObj.upperFirstCamelCase},`,            "<!-- Don't touch me - place component -->": `<${answers.nameObj.upperFirstCamelCase}/>`,          });        },        [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`]: function (          content,          options        ) {          return api.replace(content, {            Template: `${answers.nameObj.upperFirstCamelCase}`,          });        },      };    },  },};


丹尼尔: 完美。谢了蛋兄,我感觉我已经打道任督二脉,跃跃欲试我的第一个代码生成器了


蛋先生:不谢,期待你的反馈


写在最后

以上 - 完整配置

例子中 ncgen-config.js 的完整配置请查看:

https://github.com/daniel-dx/vue3-ncgen-demo/blob/master/ncgen-config.js

以下 - ncgen 官网

https://daniel-dx.github.io/ncgen/




关键字:ncgen, scaffolding, generator, 代码生成器, 脚手架

目录
相关文章
|
6月前
|
算法 Java
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
jvm性能调优 - 15JVM的老年代垃圾回收器CMS的缺点
119 0
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
62 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
3月前
|
监控 算法 JavaScript
介绍一下 V8 的内存管理,包括新生代和老生代。
【8月更文挑战第31天】介绍一下 V8 的内存管理,包括新生代和老生代。
42 2
|
6月前
|
Arthas 监控 算法
JVM工作原理与实战(二十五):堆的垃圾回收-垃圾回收算法
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收算法评价标准、标记清除算法、复制算法、标记整理算法、分代垃圾回收算法等内容。
78 0
JVM工作原理与实战(二十五):堆的垃圾回收-垃圾回收算法
|
算法 Java
Java 最常见的试题:新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
Java 最常见的试题:新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
|
6月前
|
算法 Java 应用服务中间件
jvm性能调优 - 13JVM的年轻代垃圾回收器ParNew原理
jvm性能调优 - 13JVM的年轻代垃圾回收器ParNew原理
128 1
|
6月前
|
算法 Java
jvm性能调优 - 09白话新生代垃圾回收算法
jvm性能调优 - 09白话新生代垃圾回收算法
65 0
|
存储 监控 算法
纯C手写内存池
纯C手写内存池
51 0
|
算法 Java 对象存储
深入理解JVM系列教程(04) - 垃圾回收机制(二) - 垃圾回收算法
深入理解JVM系列教程(04) - 垃圾回收机制(二) - 垃圾回收算法
213 0