若是你能耐心看完這個對話,
也許你會想動動手配置一個代碼生成器,
這樣你的擼碼生活可能今後就會有所變化,變得愈發輕鬆。
丹尼爾: 最近要搞個代碼生成器,可以快速生成項目代碼那種,蛋兄有什麼推薦?vue
蛋先生: 過往一直使用 yeoman,快超 10k star 的開源項目。可是,今天要推薦給你的,並不是 yeoman,而是一個新生代的小鮮肉 ncgen
,它可能顯得更加的平易近人。git
丹尼爾: 我最最喜歡簡單的了,這個咋用呢?github
蛋先生: 老規矩,你說你的需求,我試着一一解答npm
丹尼爾: 我有一個項目模板(好比:vue3-ncgen-demo),我但願新建的項目都來自於這個項目模板,這樣我只需專心維護好這個項目模板便可。json
蛋先生: OK,這就是項目腳手架的功能了。咱們來看下 ncgen
是如何處理的。api
第一步 安裝 ncgen
ui
$ npm i ncgen -g # yarn global add ncgen
第二步 生成配置文件 ncgen-config.js
,該配置文件描述了代碼生成器的邏輯this
$ ncgen genConf
第三步 修改 ncgen-config.js
中的 main.tmplSource
爲項目模板的地址。spa
export default { main: { tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git", }, };
運行一下試試:設計
ncgen ncgen-config.js
丹尼爾: 哎呦不錯哦。不過目前生成的項目跟項目模板是如出一轍的,但它總會有屬於本身的跟項目模板不一些的信息,好比項目名稱,做者姓名等。這些我可不但願每次生成完項目還要手工修改哦。
蛋先生: OK,要求很是合理。因爲這些信息是建立項目的人才能提供,因此咱們須要經過一些問題來收集這些信息,而後就能夠根據這些信息對生成的項目做一些修改。咱們修改下 ncgen-config.js
中的 main.prompt
和 main.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, }); }, }, } };
丹尼爾: 咦,我注意到這裏並沒使用模板引擎,而是直接使用字符串替換
蛋先生: 是的,這個設計有很大的意義。用了模板引擎來替換文件,可能會致使項目模板自身沒法正常運行,由於模板引擎須要佔位符,而佔位符可能會致使代碼解析錯誤
丹尼爾: 也是,這樣一來項目模板就是一個普通的項目而已,也不用特地去作一些模板佔位符的改造
丹尼爾: 那我繼續。個人項目模板裏面放了一些模板目錄和文件(好比模塊模板目錄,組件模板文件),但我不想在生成的項目裏面看到這些模板的東西。
蛋先生: OK,沒問題,就是須要刪除指定的文件和目錄。咱們修改下 ncgen-config.js
中的 main.removeFiles
。
示例說明:
刪除生成項目中的ncgen-config.js
和src/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.welcome
和 main.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`, }; }, }, }, };
丹尼爾: 漂亮。不過對於已經存在的項目,這些項目並不是來自項目模板,而我也想加一些子命令來爲項目生成部分代碼,昨整?
蛋先生: 子命令支持兩種添加文件的方式,一種就是上面提到的來自於項目模板的代碼模板,還有一種就是你動態建立。二者可同時使用。如下示例演示如何動態建立代碼文件
示例說明(假設 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/...
關鍵字:ncgen, scaffolding, generator, 代碼生成器, 腳手架