腳手架,這個名詞關於作為前端的我們來講,或許並不生疏吧,像vue-cli,react-native-cli等,全局裝置后,只須要在敕令行中敲入一個簡樸的敕令,便可幫我們疾速的天生一個初始項目,如vue init webpack projectName,即可天生一個初始的vue項目。
本文主假如引見開闢一個簡樸的腳手架,相識開闢的基礎流程、終究經由歷程npm link鏈接到全局包。
<!– more –>
腳手架的作用
1、削減時刻,沒必要從零最先搭建初始項目,進步開闢效力。
2、便於多人合作。
3、項目更新同步輕易,只須要更新代碼庫中項目模板,即可下載最新的項目。
node相干基礎知識
在最先項如今,先簡樸引見下node相干的一些基礎知識,經由歷程npm init初始化一個node項目時,會天生一個package.json的設置文件,包含項目稱號、版本、作者、依靠等相干信息,主要說一下个中的bin字段。
許多包都有一個或多個可實行的文件,願望放在PATH中,(現實上,就是這個功用讓npm可實行的)。
當你要用這個功用時,須要給package.json中的bin字段增添一個敕令名,並指向須要實行的文件(即後文的進口文件)。初始化的時刻npm會將他鏈接到prefix/bin(全局初始化)或許./node_modules/.bin/(當地初始化)。
比方,npm有:
{ "bin" : { "npm" : "./npm-cli.js" } }
所以,當你初始化npm,它會建立一個標記鏈接到npm-cli.js劇本到/usr/local/bin/npm。
假如你只要一個可實行文件,而且名字和包名一樣。那末你能夠只用一個字符串,比方:
{ "name": "my-program"
, "version": "1.2.5"
, "bin": "./path/to/program" }
結果和這個一樣:
{ "name": "my-program"
, "version": "1.2.5"
, "bin" : { "my-program" : "./path/to/program" } }
想要相識package.json更多的細緻設置,請參考這篇文章
思緒
要開闢腳手架,起首要理清思緒,腳手架是怎樣事情的?我們能夠自創 vue-cli 的基礎思緒。vue-cli 是將項目模板放在 git 上,運轉的時刻再依據用戶交互下載差別的模板,經由模板引擎襯着出來,天生項目。如許將模板和腳手架星散,就能夠各自保護,縱然模板有變動,只須要上傳最新的模板即可,而不須要用戶去更新腳手架就能夠天生最新的項目。那末就能夠根據這個思緒來舉行開闢了。
初始化項目
新建一個文件夾,翻開敕令行東西,經由歷程npm init 舉行項目初始化,會在項目根目次下天生package.json文件。
npm init
裝置依靠
npm install commander download-git-repo inquirer handlebars ora chalk log-symbols shelljs -S
除此以外,還運用了nodejs的幾個內置模塊:fs、path、child_process
commander.js:能夠自動的剖析敕令和參數,用於處置懲罰用戶輸入的敕令。
download-git-repo:下載並提取 git 堆棧,用於下載項目模板。
Inquirer.js:通用的敕令行用戶界面鳩合,用於和用戶舉行交互。
handlebars.js:模板引擎,將用戶提交的信息動態填充到文件中。
ora:下載歷程久的話,能夠用於顯現下載中的動畫結果。
chalk:能夠給終端的字體加上色彩。
log-symbols:能夠在終端上顯現出 √ 或 × 等的圖標。
fs:node內置的文件處置懲罰模塊。
path:node內置的途徑處置懲罰、剖析模塊。
child_process:node中建立子歷程模塊。
設置進口文件
1、在package.json文件中增添bin字段,臨時先命名為okcli吧。
"bin": {
"okcli": "./index.js"
}
2、在項目根目次新建index.js文件,在index.js文件頂部增添以下代碼:
#!/usr/bin/env node
即可舉行開闢。
在這裏,簡樸引見下這行代碼寄義。
<font color=#f00>
!/usr/bin/node是通知操縱體系實行這個劇本的時刻,挪用/usr/bin下的node詮釋器;
!/usr/bin/env node這類用法是為了防備操縱體系用戶沒有將node裝在默許的/usr/bin途徑里。當體系看到這一行的時刻,起首會到env設置里查找node的裝置途徑,再挪用對應途徑下的詮釋器順序完成操縱。
!/usr/bin/node相當於寫死了node途徑;
!/usr/bin/env node會去環境設置尋覓node目次,引薦這類寫法
</font>
處置懲罰敕令行
const program = require('commander');
const inquirer = require('inquirer');
const symbols = require('log-symbols');
const download = require('download-git-repo');
const handlebars = require('handlebars');
const chalk = require('chalk');
const ora = require('ora');
const shell = require('shelljs');
const child_process = require('child_process');
const fs = require('fs');
const path = require('path');
program.version('1.0.0', '-v, --version')
.command('init <name>')
.action((name) => {
console.log(name);
});
program.parse(process.argv);
挪用program.version(‘1.0.0’, ‘-v, –version’)會將-v和–version增添到敕令行中,挪用時可經由歷程帶上該參數獵取該腳手架的版本號(敕令 -v/–version),挪用comand(‘init <name>’)定義初始化敕令,name參數必傳,作為項目的文件夾名,如vue init webpack parojectName
action是實行command敕令時發作的回調,參數為敕令行中輸入的name,即init <name>中的name,項目天生歷程便發作在回調函數中。
如今能夠經由歷程挪用node index.js init test,能夠看到掌握台中已打印了輸入的項目名,也就是test。
个中:program.parse(process.argv)剖析敕令行中的參數,剖析出name,並傳入action回調。
下載模板
經由歷程download-git-repo或許直接運用shelljs或許child_process直接運轉敕令舉行下載模塊(個人挑選的是第三種)。
download-git-repo 支撐從 Github、Gitlab 和 Bitbucket 下載堆棧,各自的詳細用法能夠參考官方文檔。
download('https://github.com/jefferyE/webpack-configuration-for-vue', name, {clone: true}, (err) => {
if(err){
// spinner.fail();
console.log(symbols.error, chalk.red(err));
}else{
// spinner.fail();
}
})
或許
if (shell.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue').code == 0) {
// spinner.succeed();
} else {
// spinner.fail();
console.log(symbols.error, chalk.red('模板下載失利'))
}
或許
child_process.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue', function(err, stdout, stderr) {
if (err) {
// spinner.fail();
console.log(symbols.error, chalk.red('模板下載失利'))
} else {
// spinner.succeed();
}
})
个中:download() 第一個參數就是堆棧地點,但是有一點點不一樣。現實的堆棧地點是 https://github.com/jefferyE/w… ,能夠看到端口號背面的 ‘/‘ 在參數中要寫成 ‘:’,#master 代表的就是分支名,差別的模板能夠放在差別的分支中,變動分支便能夠完成下載差別的模板文件了。第二個參數是途徑,上面我們直接在當前途徑下建立一個 name 的文件夾寄存模板,也能夠運用二級目次比方 test/${name}
交互敕令行
敕令行交互功用能夠在用戶實行 init 敕令后,向用戶提出題目,吸收用戶的輸入並作出響應的處置懲罰。這裏運用 inquirer.js 來完成,也能夠本身運用node內置的readline模塊。
詳細能夠參考我的這篇文章
inquirer.prompt([
{
type: 'input',
name: 'author',
message: '請輸入作者稱號'
}
]).then((answers) => {
console.log(answers.author);
})
經由歷程這裏例子能夠看出,題目就放在 prompt() 中,題目的範例為 input 就是輸入範例,name 就是作為答案對象中的 key,message 就是題目了,用戶輸入的答案就在 answers 中,運用起來就是這麼簡樸。更多的參數設置能夠參考官方文檔。
襯着package模板
這裏用 handlebars 的語法對 HTML5/H5Template 堆棧的模板中的 package.json 文件做一些修正。並在下載模板完成以後將用戶輸入的答案襯着到 package.json 中。
{
"name": "{{name}}",
"version": "1.0.0",
"description": "{{description}}",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "{{author}}",
"license": "ISC"
}
注重:因為我的項目沒有變動package模板,因而經由歷程在下載模板后,剖析時舉行動態修正的。
除此以外,經由歷程ora、chalk模塊也舉行了一些視覺美化。詳細請參考完全代碼。
完全代碼以下:
#!/usr/bin/env node
const program = require('commander');
const chalk = require('chalk');
const ora = require('ora');
const fs = require('fs');
const inquirer = require('inquirer');
const shell = require('shelljs');
const symbols = require('log-symbols');
const download = require('download-git-repo');
const child_process = require('child_process');
const handlebars = require('handlebars');
const path = require('path');
program.version('1.0.0', '-v, --version').
command('init <name>').
action(name => {
console.log(name);
if (!fs.existsSync(name)) {
console.log('正在建立項目...');
inquirer.prompt([
{
name: 'description',
message: '請輸入項目形貌'
},
{
name: 'author',
message: '請輸入作者稱號'
}
]).then(answers => {
console.log(answers)
const spinner = ora('正在向下載模板...\n');
spinner.start();
child_process.exec('git clone https://github.com/jefferyE/webpack-configuration-for-vue', function(err, stdout, stderr) {
if (err) {
spinner.fail();
console.log(symbols.error, chalk.red('模板下載失利'))
} else {
spinner.succeed();
shell.mv(__dirname + '/webpack-configuration-for-vue', __dirname + '/' + name)
const filename = `${name}/package.json`;
const meta = {
name,
description: answers.description,
author: answers.author
}
if (fs.existsSync(filename)) {
const content = fs.readFileSync(filename).toString();
let dt = JSON.parse(content);
dt.name = '{{name}}';
dt.description = '{{description}}'
const result = handlebars.compile(JSON.stringify(dt, null, 2))(meta);
fs.writeFileSync(filename, result);
console.log(symbols.success, chalk.green('項目初始化完成'));
} else {
console.log(symbols.error, chalk.red('package不存在'))
}
}
})
})
} else {
console.log(symbols.error, chalk.red('項目已存在'));
}
})
program.parse(process.argv);
个中:
1、在用戶輸入答案以後,最先下載模板,這時刻運用 ora 來提醒用戶正在下載中。
2、然後經由歷程 chalk 來為打印信息加上款式,比方勝利信息為綠色,失利信息為赤色,如許子會讓用戶越發輕易區分,同時也讓終端的顯現越發的悅目。
3、除了給打印信息加上色彩以外,還能夠運用 log-symbols 在信息前面加上 √ 或 × 等的圖標。
完成以後,就能夠把腳手架宣布到 npm 上面,經由歷程 -g 舉行全局裝置,就能夠在本身本機上實行 okcli init [name] 來初始化項目,如許便完成了一個簡樸的腳手架東西了。
鏈接全局敕令
在根目次運轉 npm link敕令,將包鏈接到全局環境。
npm link敕令能夠將一個恣意位置的npm包鏈接到全局實行環境,從而在恣意位置運用敕令行都能夠直接運轉該npm包。
詳細請參考這篇文章