plopjs是一個基於node js所開發的小工具,主要作用是根據模板代碼生成目標代碼。
plop對於模板代碼的處理選擇了 handlebars 作為模板
目的
通過自動化工具減少開發中重復代碼的書寫
使用舉例
新建一容器組件用於展示消息列表,組件名為message
使用plop前
- 在項目的適當目錄下新建message/index.js文件,message/index.less文件。
- 在message/index.js文件內寫出react組件的基礎代碼,並在message/index.less內書寫默認樣式
使用plop之后(需要提前進行相關的配置)
- 項目根目錄,終端運行plop,根據提示輸入組件名message
- 添加路由配置文件(這步驟應該也可以由plop完成)
對比
- 手動操作:通常我的做法是復制現有的組件,然后重命名,不過有時候涉及到的文件可能更多,比如redux的action與reducer文件。由於是手動操作,有時候也可能忘記了對於某個地方的修改。
- 使用plop:終端操作只需要在命令行進行適當的交互就可以,自動生成對應文件,不會出錯並且快速。
應用場景
- 【前端】vue/react框架新建新的頁面
- 【前端】根據接口文檔,自動生成有關的代碼(比如發送請求的函數)?
- 【前端】redux有關代碼的自動生成?
- 對於某些不方便抽象的重復代碼。比如前端某些類似的表格、表單、頁面布局、類似的controller等,可以抽象為模板的代碼
- 【小程序】在使用非官方開發工具時,通過腳本來創建組件/頁面,從而實現類似於微信開發者工具,一次性創建出wxml,wxss,js,json文件。並且可以自定義這些模板。
- 其他與模板有關的功能。
注:plop.js的終端交互使用了Inquirer.js,所以也可以使用inquirer的插件對其功能進行擴展(比如選擇文件夾)
開始
- 安裝npm包
npm i plop -g npm i plop -D
- 在項目的根目錄創建配置文件plopfile.js
// 配置文件主要是exports了一個函數 module.exports = function (plop) { // controller generator plop.setGenerator('controller', { description: 'application controller logic', prompts: [{ type: 'input', name: 'name', message: 'controller name please' }], actions: [{ type: 'add', path: 'src/{{name}}.js', templateFile: 'plop-templates/controller.hbs' }] }); };
- 創建 plop-templates/controller.hbs文件。並寫一些內容。
- 在項目根目錄下,執行 plop,然后根據終端命令提示進行操作即可,最終會發現自動生成了 src/{{name}}.js 文件,文件的內容就是模板的內容。
Plopfile Api
配置文件是支持ts的,具體的使用方法請參考官網
setGenerator
此函數接受兩個參數,第一個參數是設置的Generator的名字,第二個config對象需要包括提示和操作(description是可選的)。prompts數組被傳遞給inquirer。actions數組是要執行的操作的列表。
- 典型的配置如下:
// 示例 module.exports = function (plop) { plop.setGenerator('fc_page', { description: 'application fc_page logic', // 可選字段,會顯示在控制台 prompts: [{ type: 'input', // 用來在終端輸入文本 name: 'name', // 在 actios 內 可以通過{{name}} 取到這里輸入的值,在模板內也可以通過{{name}}取到這個值 message: '請輸入函數組件的名字(必填)', // 會顯示在控制台 validate(val) { // 如果驗證不通過,需要返回字符串,字符串將會被展示在控制台 // 驗證通過,則返回true return true; }, default: "default", // 默認值 }], actions:[{ // 添加文件 src/components/{{name}}/index.js 文件內容為templateFile指定的內容 type: 'add', path: 'src/components/{{name}}/index.js', templateFile: 'plop-templates/functionComponentWithLessPage.hbs', // 如果模板文件內容少,可以直接使用template指定 // template:"" // 在將文件輸出到硬盤前會調用transform函數 // transform:()=>"" // 當運行這個actions,data會合並到prompt answers。 // data:{} //...其他不常用參數請查看文檔 },{ // 修改src/index.js文件中的內容,將 pattern 匹配到的內容,修改為 template 的內容 type: 'modify', path: 'src/index.js', pattern: /(\/\* injectinfo \*\/)/, template: '$1\nconsole.log(111);', },{ // 修改src/index.js文件中的內容,在 pattern 匹配到的內容后追加 template 的內容 type: 'append', path: 'src/route.config/index.js', pattern: /\/\* inject \*\//, template: 'console.log(666);', }], } };
prompts
- prompts數組里的type除了可以是input,還可以是
input
,number
,confirm
,list
,rawlist
,expand
,checkbox
,password
,editor
。當然也是支持inquirer插件的。
prompts數組會直接傳遞給inquirer,所以更多配置請參考 https://github.com/SBoudrias/Inquirer.js#objects
actions
- actions數組內的type,plop默認提供了4中Add,AddMany,Modify,Append。
- 如果actions需要根據prompts的answer來決定,那么可以使用動態actions,示例如下。
// 示例 module.exports = function (plop) { plop.setGenerator('fc_page', { description: 'application fc_page logic', prompts: [{ type: 'confirm', name: 'needLess', message: '是否需要樣式文件(默認需要)', default:true, }], actions(data) { if (data.needLess) { return []; } return []; }, }); };
setPrompt
用於使用inquirer插件來擴展prompts
// 為 prompt 增加目錄選擇功能 const promptDirectory = require('inquirer-directory'); module.exports = function (plop) { plop.setPrompt('directory', promptDirectory); plop.setGenerator('test', { prompts: [{ type: 'directory',
setActionType
在plopfile中添加自定義的ActionType(類似於add,addMany)
module.exports = function (plop) { plop.setActionType('doTheThing', function (answers, config, plop) { // do something doSomething(config.configProp); // if something went wrong throw 'error message'; // otherwise return 'success status message'; }); // or do async things inside of an action plop.setActionType('doTheAsyncThing', function (answers, config, plop) { // do something return new Promise((resolve, reject) => { if (success) { resolve('success status message'); } else { reject('error message'); } }); }); // use the custom action plop.setGenerator('test', { prompts: [], actions: [{ type: 'doTheThing', configProp: 'available from the config param' }, { type: 'doTheAsyncThing', speed: 'slow' }] }); };
setHelper
module.exports = function (plop) { plop.setHelper('upperCase', function (text) { return text.toUpperCase(); }); // or in es6/es2015 plop.setHelper('upperCase', (txt) => txt.toUpperCase()); };
setPartial
module.exports = function (plop) { plop.setPartial('myTitlePartial', '<h1>{{titleCase name}}</h1>'); // used in template as {{> myTitlePartial }} };
load
loads generators, helpers and/or partials from another plopfile or npm module
https://github.com/plopjs/plop/blob/master/plop-load.md
other-methods
- setWelcomeMessage:自定義WelcomeMessage
- ...其它的請去官網查看
高級
prompts的提示
https://plopjs.com/documentation/#3rd-party-prompt-bypass
將plop集合到自己的CLI項目中
https://plopjs.com/documentation/#wrapping-plop
思考
通過內置的action和自己擴展的action,加上前端的一切周邊工具,比如nodejs、babel,基本可以使得plop可以實現任何的功能,不過具體是先到什么程度,怎樣去用還需要根據具體的項目去權衡。
如果將toB的前端項目的業務邏輯抽離出代碼模板,那么我想應該是可以實現如下功能的。
比如:
- 從一個表格頁的模板自動生成一個純展示的表單頁。我們只需要配置好接口和需要展示的內容,比如頁面的邏輯代碼,ajax請求有關代碼的自動生成,路由配置文件的自動修改等?之后在針對於代碼做修改,即可實現需求
- 我們需要創建一個表單頁,是否可以直接通過命令行指定下,與表單相關的接口,然后就將表單有關的代碼自動生成出來,之后再在模板代碼上做修改以實現具體的需求
這樣可以將更多的工作去交給自動化去處理。大量提升開發效率。不過,事情沒有面面俱到的,可能做之前還要結合實際的場景來決定適用到什么程度。
比如:
- 項目的功能模塊的相似程度,比如toB的業務往往更容易抽象出模板,且模板也更加通用。
- 對於原有項目的代碼侵入性,因為如果涉及到修改現有文件,可能需要對現有文件做標記,比如在js中添加特殊的注釋。
- 項目處於的階段,比如是高速迭代,還是維護。在實際的開發中是否會有大量的類似代碼對我們的開發工作造成困擾。
- 業務邏輯是否復雜多變。
對於這個工具,我感覺做一些類似於頁面的代碼模板生成還是挺好的,尤其時對於大多數toB項目來說,每個頁面的邏輯其實差距不大,主要也就是列表、表單、表格、之類的東西。通過命令行交互來代替之前的復制、粘貼、手動修改,我想還是會提高一定的效率的。