腳手架工具
常用腳手架工具
- React項目create-react-app
- Vue項目vue-cli
- Angular項目angular-cli
- Yeoman通用型腳手架工具
- Plop創建一個組件/模塊所需要的文件(類似於Yeoman的sub-generator)
腳手架工具的工作原理
使用node.js來開發一個小型腳手架。
- 創建sample-scaffolding文件夾,使用
npm init
初始化 - 在package.json中添加
”bin“: "cli.js"
用於指定該腳手架的命令行入口文件爲cli.js。關於Node命令行參考阮一峯老師的文章"bin": { "sample-scaffolding": "cli.js" }
表示sample-scaffolding是在終端輸入的命令,該命令的真實入口文件是當前目錄下的cli.js。"bin"字段用來指示命令名與可執行腳本文件的映射關係。
- 創建模板:在項目根路徑下創建templates文件夾來存儲模板文件,如這裏創建了templates/index.html和templates/style.css。
- cli.js的內容:
- Node CLI應用入口文件必須要有
#!/usr/bin/env node
文件頭 - 如果是Linux或者MacOS系統,則需要修改文件的權限爲755(文件擁有者可讀可寫可執行)
chmod 755 cli.js
- 業務代碼
- 通過命令行交互詢問用戶問題,安裝
npm install inquirer
包 - 根據用戶回答的結果生成文件,需要安裝模板引擎
npm install ejs
來解析模板文件
- 通過命令行交互詢問用戶問題,安裝
- Node CLI應用入口文件必須要有
// cli.js
const inquirer = require('inquirer');
const ejs = require('ejs');
inquirer.propmt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
])
.then(answers => {
// 根據用戶回答的結果answers結合模板文件,生成新文件
// 模板目錄
const tmplDir = path.join(__dirname, 'templates');
// 目標目錄, 這裏是程序執行的當前路徑
const destDir = process.cwd();
// 將模板下的模板文件全部輸出到目標目錄
fs.readdir(tmplDir, (err, files) => {
if(err) {
throw err;
}
files.forEach(file => {
// 通過模板引擎渲染模板文件,將結果寫入新的文件中。
ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => {
if(err) throw err;
// 將結果寫入文件
fs.writeFileSync(path.join(destDir, file), result);
})
})
})
})
- 使用
npm link
全局註冊 - 在命令行中使用
sample-scaffolding
命令執行
Yeoman
基本使用
- 全局安裝yeoman:
npm install yo -g
- 全局安裝generator:安裝項目對應的generator包,讓yeoman運行generator包來創建項目基本結構。yeoman + generator包 = 腳手架工具。比如安裝generator-node來創建一個node項目:
npm install generator-node -g
- 運行腳手架工具:
yo node
,注意要去掉前綴generator
sub-generator
在已有項目中再創建一些項目文件(結構),就是在generator創建的項目中繼續generator一些文件,就是sub-generator。
- 使用方式(以上面的node項目爲例):
yo node: cli
- 語法:
yo <generator-name>: <sub-generator-name>
- 注意:並非所有的generator包都包含sub-generator(根據generator文檔查看),有的時候需要我們自己去定義sub-generator
npm安裝二進制文件時的鏡像配置
有時候npm安裝二進制包時,通過對應包的鏡像配置來加速下載。
創建自定義generator
- Generator本質上就是一個NPM模塊
- Generator包含兩個組成部分
- 組裝指令
- 模板文件
- Generator需要特定的文件結構
- app/index.js爲生成器組裝指令文件
- sub-generator則在該Generator目錄內與app目錄同級,例如這裏的component目錄
- generator的名稱要求是有generator-前綴
generator-<name>
,否則yeoman是無法找到的。
創建generator
- 新建文件夾作爲自定義generator的項目根路徑,文件夾命名爲generator-<name>
- 使用
npm init
初始化package.json文件,注意需要手動更改一些信息。
- 安裝最新版的yeoman-generator作爲依賴
npm install yeoman-generator -S
- 在項目文件夾中,創建generator/app/index.js文件,作爲generator的核心入口。
- index.js需要導出一個繼承自Yeoman Generator的類型
- Yeoman Generator會在工作時自動調用此類型中定義的一些生命週期方法
- 在這些生命週期方法中,通過調用父類的API實現功能,如文件寫入等
- 完成generator後,在項目根路徑使用
npm link
命令將generator包鏈接到本地的全局npm包所在地,這樣就相當於使用了全局安裝。
使用自定義generator
- 新建文件夾作爲項目根路徑
- 使用
yo <name>
來使用自定義的generator作爲腳手架工具來生成項目結構。
使用模板文件來創建自定義generator
- 在app/目錄下創建一個templates/目錄,用於存放模板文件
- 模板文件使用的是EJS模板引擎的語法規範
- 同時,在index.js中需要使用根據模板方式創建項目的API方法
接收用戶輸入的數據
有很多時候需要根據用戶輸入的信息,來決定如何生成項目的結構。比如模板文件中的動態數據、是否安裝某項依賴等等
命令行詢問
在index.js中,導出的類裏添加prompting方法,yeoman在詢問用戶的階段會自動調用此方法,在prompting方法中可以調用父類的prompt方法來發出對用戶的命令行詢問。
module.exports = class extends Generator {
prompting() {
// 返回一個Promise,yeoman可以使用異步流程
// this.prompt()接收一個數組,數組元素爲表示要提出的問題對象
//
return this.prompt([
// 以下這個示例表示的是請用戶輸入項目名稱,默認是該項目所在文件夾名稱
{
type: 'input', // 問題類型,這裏表示使用用戶輸入的方式提交信息
name: 'name', // 類似於web表單的name屬性,也就是結果的鍵值對中的鍵
message: 'your project name', // 該問題的描述
default: this.appname // 該問題的默認值,我們這裏使用了this.appname表示爲項目生成的目錄名稱
}
])
// answers爲當前問題的用戶回答
.then(answers => {
// 將用戶給與的回答掛載到answers屬性上,以便於之後使用該信息。
this.answers = answers;
})
}
}
創建自定義的Vue腳手架
Plop:小而美的腳手架工具
創建特定類型文件的腳手架工具,有點類似yeoman sub-generator。一般不獨立使用,而是集成在項目中,自動化地創建同類型文件。
使用
- 安裝:
npm install plop -D
- 項目根目錄下創建plopfile.js文件作爲Plop的入口文件
- 需要導出一個函數
- 該函數接受一個Plop對象,用於創建生成器任務
- 項目根目錄下創建plop-templates目錄,用於存放Plop生成文件時的模板文件
- 模板文件遵循handlebars的模板語法
- 在命令行中啓動Plop:
npx plop <task-name>
,這裏的<task-name>就是在plopfile.js中定義的生成器任務名字。啓動的方式與gulp在命令行啓動任務是一致的。
// plopfile.js
module.exports = plop => {
// 創建生成器任務
// setGenerator第一個參數爲生成器名字,第二個參數爲配置選項
plop.setGenerator('component', {
// 對於任務的一個描述
description: 'create a component',
// 對於該任務,在命令行中的交互問題
prompts: [
{
type: 'input',
name: 'name',
message: 'component name',
default: 'MyComponent'
}
],
// 完成命令行交互後生成器的動作,多個文件多個模板如下
actions: [
{
type: 'add', // 表示添加新的文件
// 表示添加文件的路徑,可以使用{{}}的插值語法,下面的name就是命令行獲得的name值
path: 'src/components/{{name}}/{{name}}.js',
// 指定模板文件的路徑
templateFile: 'plop-templates/component.hbs'
},
{
type: 'add', // 表示添加新的文件
// 表示添加文件的路徑,可以使用{{}}的插值語法,下面的name就是命令行獲得的name值
path: 'src/components/{{name}}/{{name}}.css',
// 指定模板文件的路徑
templateFile: 'plop-templates/component.css.hbs'
},
{
type: 'add', // 表示添加新的文件
// 表示添加文件的路徑,可以使用{{}}的插值語法,下面的name就是命令行獲得的name值
path: 'src/components/{{name}}/{{name}}.test.j.s',
// 指定模板文件的路徑
templateFile: 'plop-templates/component.test.hbs'
}
]
})
}