基於 Yeoman 定製的互動式命令列腳手架

橙鄉果汁發表於2016-09-26

腳手架這個詞估計做前端的都很熟悉。在沒有實現前端工程化的年代,前端程式碼的組織都是純手工維護的。比如我要做一個網站頁面,那麼我需要手動建立一個資料夾來存放程式碼檔案,我把它命名為demo。然後在demo目錄下建立src資料夾,在src資料夾內建立css資料夾、js資料夾、image資料夾、lib資料夾等等…一切都是手工維護。自從node.js出現後,前端開發才慢慢開始告別刀耕火種,越來越多的自動化工具充斥我們的眼球。模板生成、程式碼壓縮、構建打包、自動部署…這些已經成為構建前端工程專案的標配。那麼,一個模板生成的命令列工具的原理是什麼?怎樣開發一個屬於自己的命令列腳手架工具?希望我寫的這篇小文章會給大家帶來一點啟發。

原理

生成模板檔案的方式可以是本地新建空白檔案,然後進行檔案內容讀寫;又或者是把本地已有的模板進行配置資訊填充。然而我們知道,IO讀寫的速度非常慢,效能消耗大。但是一個模板生成器(Generator)如果是基於已有的模板檔案進行配置填充,然後在copy到專案目錄對應的位置,那會比直接讀寫磁碟效率更高。所以一般來說,模板生成器會採用第二種工作原理。

Yeoman-generator

模板生成器的腳手架有很多,前端領域每天都會有很多類似的輪子源源不斷地從開源社群流出。這裡我用來開發自己的generator的工具是Yeoman。Yeoman的Logo是一個戴著紅帽子的大鬍子,它是一個通用的腳手架搭建系統,可以建立任何的型別的app。同時它又是”語言無感知”的,支援建立任何型別開發語言的專案,Web, Java, Python, C# 等等。Yeoman的通用性在於,它本身不做任何決定,所有的操作都是通過Yeoman環境裡面的各種generator實現的。通過自定義generator,我們可以建立任何格式的專案目錄。這是Yeoman的最大魅力之處。另外,Yeoman通過提供promting這個方法實現輸入式命令列互動,可以讓使用者自由填寫配置資訊,互動體驗也非常棒。下面說說怎樣基於Yeoman開發一個簡單的generator:

Simple-dir

simple-dir是我自己搗鼓的一個很簡單的Yeoman-generator,在這裡我拿它來作為講解示例,大家也可以開啟詳細程式碼來看,歡迎star,也歡迎提issue。

第一步,package.json

開發一個Yeoman-generator,我們要做的第一步就是配置package.json。有幾個關鍵的地方,一個是,name的值的格式必須是”generator-“字首 + Yeoman-generators官方源列表上的唯一值(如果你要共享你的generator到官方generator源的話);第二個就是,keywords屬性必須包括”yeoman-generator”這個值;第三,files屬性是命令自定義檔案,app是預設的命令;第四,必須要安裝最新版本的yeoman-generator依賴,可以直接執行:npm install –save yeoman-generator 獲取最新的版本號。詳細的package.json可以看下面這份:

對應的src目錄格式應該是這樣的:

├───package.json └───generators/ ├───app/ │ └───index.js ├───comp/ │ └───index.js └───page/ └───index.js

你也可以直接把files屬性直接寫成:

但是這樣的話,你的程式碼根目錄就必須直接包含app,comp和page資料夾。

第二步,擴充generator

這裡我們有三個generator——app,comp和page。以page為例,我們來實現一個generator。

首先,需要繼承Yeoman提供的generator基類:

然後我們就可以在基類內部重寫generator的方法了。Yeoman提供了一系列的基類方法:

initializing – 初始化 (檢查當前專案狀態、獲取配置檔案內容等等) prompting – 獲取使用者輸入,實現與使用者的互動 (通過this.prompt()呼叫) configuring – 儲存配置並配置整個專案 (比如建立 .editorconfig 檔案和其他媒介檔案) default – 當定義的方法沒有匹配任何基類方法的時候用到 writing – 根據自定義的規則寫入具體的generator檔案 (routes, controllers, etc) conflicts – 內部衝突處理 install – 安裝npm、bower等依賴的地方 end – 在最後呼叫, 實現cleanup, say good bye等功能。

在示例generator-simple-dir裡,page這個generator的作用是建立頁面,需要生成html/css/js檔案。在generators.Base.extend函式內部,page實現了 initializing、prompting、writing、end這幾個方法。對於prompting這樣的非同步方法,需要在互動結束的時候呼叫this.async()來結束非同步任務。Yeoman實現使用者互動的核心方法是prompting,它是一個非同步的方法,並且返回一個promise。prompting方法通過一個陣列引數,可以實現鏈式的使用者輸入。其中input型別的是使用者輸入自定義內容,confirm型別是作為True/False判斷的prompt,輸入Y/N。官方的示例如下:

如果你想要記住使用者輸入的一個內容,用來做後面輸入的預設值的話,還可以通過增加store:true配置來實現。 在generator-simple-dir裡面,page這個generator包含4個執行步驟:初始化、獲取使用者輸入、根據使用者輸入生產模板檔案、結束返回,實現的程式碼如下:

定製模板

prompting方法是用來獲取使用者輸入,writing方法是根據使用者輸入內容生成模板檔案。之前說到,模板生成器的一般原理是用獲取的配置資訊渲染好模板,再拷貝到專案目錄對應的位置。所以,在writing方法裡面,需要實現模板渲染和拷貝。在Yeoman-generator裡,需要的模板檔案預設放在templates資料夾裡,所有檔案相關的操作通過this.fs物件來實現。this.fs.copyTpl就是我們用來拷貝渲染好的模板檔案的方法,需要輸入三個引數:模板源路徑、需要拷貝到的專案路徑、模板渲染內容物件。模板的渲染是基於ejs模板引擎的語法。根據我們定義的專案結構,page的實現如下:

更詳細的Yeoman-generator檔案操作文件請點選這裡

commander

上面我們講解了Yeoman-generator的定製,也展示了一個簡單的generator——“simple-dir”。為了把simple-dir很優雅地跑起來,我們需要搞一個命令列工具。基於Nodejs開發自己的命令列工具是很簡單的事情,因為TJ大神已經為我們貢獻了屌炸天的工具——commander.js。關於commander的使用教程有很多,也比較容易上手,如果你還沒有了解過commander.js,推薦閱讀這兩篇文章:《Commander:node.js命令列介面的完全解決方案》《Commander寫自己的Nodejs命令》

有了commander的基礎之後,我們將Yeoman-generator封裝到自定義好的命令中。比如我已經封裝好了自己的命令列工具,它的名字叫做atdir(取自auto director),我們想要實現只需要執行 “atdir page” 就會自動生成需要的 html/css/js。然後我們只需要在atdir裡面定義page.js:

env.lookup()的作用是遍歷使用者機器上安裝好的generator,接入到Yeoman-environment,比如我們simple-dir的init、page或者comp命令。然後執行env.run()。由於我已經將simple-dir釋出到npm包了,所以可以直接呼叫env.run(‘simple-dir:page’,function(){})。如果你不想將generator釋出到npm,然後又想在本地使用generator的話也可以,直接進入generator的根目錄,執行npm link,simple-dir 指令就會關聯到本地的npm裡面,Yeoman就能找到 “simple-dir:page” 這個指令啦!

小工具——atdir

atdir就是上面說的命令列小工具,想要了解命令列的詳細封裝方法可以戳這裡。由於atdir沒有釋出到npm源,不能直接npm i。如果想要執行起來的話,請先把atdir原始碼clone到本地,進入到atdir根目錄,執行npm link,npm install 之後就可以愉快的執行atdir命令啦~~~ 附上幾張執行介面截圖: $ atdir init b729cf88363ad5db

$ atdir page 10d75b6287edabd0

$ atdir comp ac10496e5496593a

結語

這只是Yeoman-generator的簡單用法,意圖在於學習搭建一個自己的命令列腳手架,其實還有很多可以完善的地方,比如目前的模板目錄是固定的,可以考慮實現更靈活的配置;還可以加上webpack等打包工具的config實現自動構建等等,這個就留到後面再去擴充。大家有什麼想法也可以在github上提issue,歡迎指正!

相關文章