您現在的位置是:首頁 > 遊戲
前端工程化之專案腳手架
前端腳手架是什麼意思
在建築領域,腳手架是為了保證各施工過程順利進行而搭設的工作平臺。在軟體開發領域,如果把搭建專案想象成建造大型建築的話,腳手架就是為了方便大家快速進入業務邏輯的開發,一個好的腳手架能顯著提升工程效率,例如三大前端框架都提供了自己的腳手架工具:
Angular 中的 @angular/cli
Vue 中的 @vue/cli
React 中的 create-react-app
上述工具雖好,但相信很多公司為了滿足自身業務需要,也造了不少自己的輪子,約定使用自己的那一套配置,如果沒有腳手架,就只能把原專案程式碼複製過來,刪除無用的邏輯,只保留基礎能力,這個過程瑣碎且耗時。
因此,在這種情況下,就需要定製自己的開發模板,搭建一套屬於自己的前端腳手架了。
預備知識
要寫一個腳手架首先要掌握 node。js 的各種 API,然後還要充分利用別人寫好的一些類庫,例如下面就是必備的:
commander :TJ 大神的又一神作,腳手架必備工具,能夠幫我們解析命令列的各種引數,透過回撥完成具體邏輯實現。
inquirer:強大的互動式命令列工具,使用者可以在命令列進行單選或多選,也可以用 prompts 這個庫,用法和效果都是類似的。
chalk :能夠在命令列中給文字上色,從而突出重點,例如 error 用紅色,warning 用黃色,success 用綠色,視覺效果非常好。
metalsmith :靜態網站生成器,可以讀取指定資料夾下面的模板檔案,經過一系列的外掛處理,把檔案輸出到新的目錄下。
掌握了上面的工具之後,就可以寫一個自己的腳手架了。我們後端採用了 feathersjs 庫,但是不太喜歡它提供的腳手架,於是自己定製了一個,效果如下:
製作腳手架
製作腳手架整個過程分如下 5 個步驟(簡稱 cpcar):
cli 專案初始化
parse 命令列引數
clone 腳手架模板
ask 使用者專案配置
render 專案檔案
接下來逐一介紹:
cli 專案初始化
首先建立空目錄並進行初始化:
$ mkdir feathers-cli
$ cd feathers-cli
$ npm init -y
複製程式碼
然後用 vscode 開啟,為 package。json 新增 bin 欄位如下:
{
“name”: “feathers-cli”,
“main”: “index。js”,
“bin”: {
“feat”: “。/bin/feat。js”
}
}
複製程式碼
然後建立 bin 資料夾,在裡面新建一個 feat。js 檔案,內容是:
#! /usr/bin/env node
console。log(‘My custom feathers scaffold’)
複製程式碼
然後在根目錄下執行:
$ npm link
$ feat
My custom feathers scaffold
複製程式碼
到這裡,專案初始化就完成了。此時,npm 會在全域性下建立一個 feat 可執行檔案,它是一個軟連結,指向 bin/feat。js,所以後面每次修改內容,都會輸出最新的結果,不需要重新執行 npm link 命令。
parse 命令列引數
接下來需要利用 commander 來解析命令列引數,例如當用戶輸入 feat ——help 的時候能夠輸出幫助提示,首先安裝依賴包:
$ npm i commander
複製程式碼
然後修改 bin/feat。js 內容為:
#! /usr/bin/env node
const program = require(‘commander’)
program。parse(process。argv)
複製程式碼
此時輸入命令就能看到提示訊息了:
$ feat ——help
Usage: feat [options]
Options:
-h, ——help display helpforcommand
複製程式碼
這是 commander 預設幫我們新增的幫助資訊,目前還沒有配置任何的命令,接下來完善程式碼如下:
#! /usr/bin/env node
const program = require(‘commander’)
const pkg = require(‘。。/package。json’)
program
。command(‘create
。description(‘create a new project powered by feathers-cli’)
。option(‘-f, ——force’, ‘override’)
。action((name, cmd) => {
console。log(‘name’, name)
console。log(‘cmd。options’, cmd。options)
console。log(‘cmd。args’, cmd。args)
})
program。version(pkg。version)。usage(`
program。parse(process。argv)
複製程式碼
此時輸出內容就豐富多了:
$ feat ——help
Usage: feat
Options:
-V, ——version output the version number
-h, ——help display helpforcommand
Commands:
create [options]
help [command] display helpforcommand
複製程式碼
輸入 feat create xxx 的時候可以在回撥裡面獲取到相關引數:
name hello-world
cmd。options [
Option {
flags: ‘-f, ——force’,
required: false,
optional: false,
variadic: false,
mandatory: false,
short: ‘-f’,
long: ‘——force’,
negate: false,
description: ‘override’,
defaultValue: undefined
}
]
cmd。args [ ‘hello-world’ ]
複製程式碼
接下來就是完善回撥函數里面的邏輯了。
clone 腳手架模板
我們根據業務需求自己定義了一套模板 feathers-template-default,用腳手架建立專案的本質上就是把這套模板下載下來,然後再根據使用者的喜好,按照模板生成不同結構的工程檔案而已。
一般來講,模板都是放到使用者根目錄下的一個隱藏檔案中的,我們定義的目錄名為 ~/。feat-templates,首次透過 feat create xxx 的時候透過 git clone 把這套模板下載到上面定義的目錄中,後面再建立專案只需 git pull 更新即可,所以接下來就是實現倉庫的下載和更新方法了,其實就是利用 spawn 對 git 命令進行封裝:
git clone 的封裝
// 克隆倉庫
functionclone(repo, opts) {
returnnewPromise((resolve, reject) => {
const args = [‘clone’]
args。push(repo)
args。push(opts。targetPath)
const proc = spawn(‘git’, args, {cwd: opts。workdir})
proc。stdout。pipe(process。stdout)
proc。stderr。pipe(process。stderr)
proc。on(‘close’, (status) => {
if (status == 0) return resolve()
reject(newError(`‘git clone’ failed with status ${status}\n`))
})
})
}
複製程式碼
git pull 的封裝
asyncfunctionpull(cwd) {
returnnewPromise((resolve, reject) => {
const process = spawn(‘git’, [‘pull’], { cwd })
process。on(‘close’, (status) => {
if (status == 0) return resolve()
reject(newError(`‘git pull’ failed with status ${status}`))
})
})
}
複製程式碼ask 使用者專案配置
有了模板,專案主體結構就定下來了,接下來就是定義一些問題,讓使用者自己選擇專案配置:
const questions = {
projectName: {
type: ‘text’,
message: ‘專案名’,
validate: (answer) => (answer。trim() ? true : ‘專案名不能為空’),
initial: ‘my-project’,
},
projectDescription: {
type: ‘text’,
message: ‘專案描述’,
initial: ‘My Awesome Project!’,
},
needCacher: {
type: ‘toggle’,
message: ‘需要快取嗎?’,
initial: true,
active: ‘是’,
inactive: ‘否’,
},
cacher: {
type: ‘select’,
message: ‘請選擇快取方案’,
choices: [
{ title: ‘Memory’, value: ‘Memory’ },
{ title: ‘Redis’, value: ‘Redis’ },
],
when(answers) {
return answers。needCacher
},
initial: 1,
},
needWebsocket: {
type: ‘toggle’,
message: ‘需要 websocket 嗎?’,
initial: false,
active: ‘是’,
inactive: ‘否’,
},
needLint: {
type: ‘toggle’,
message: ‘需要 ESLint 嗎?’,
initial: true,
active: ‘是’,
inactive: ‘否’,
},
needJest: {
type: ‘toggle’,
message: ‘需要 Jest 嗎?’,
initial: true,
active: ‘是’,
inactive: ‘否’,
},
}
複製程式碼
然後透過一個迴圈進行遍歷,挨個詢問:
asyncfunctionask(questions, data) {
const names = Object。keys(questions)
for (let i = 0; i < names。length; i++) {
const name = names[i]
const value = questions[name]
// 拿到問題,然後組裝成 Inquirer 或 prompts 所需要的格式
const question = { /* 省略組裝程式碼 */ }
const answer = await prompts(question)
Object。assign(data, answer)
}
}
複製程式碼render 專案檔案
模板引擎有很多,例如 ejs、handlebars 等都可以用,在這裡以 handlebars 為例,先定義兩個幫助函式:
Handlebars。registerHelper(‘if_eq’, function (a, b, opts) {
return a === b
? opts。fn(this)
: opts。inverse(this)
})
Handlebars。registerHelper(‘unless_eq’, function (a, b, opts) {
return a === b
? opts。inverse(this)
: opts。fn(this)
})
複製程式碼
然後透過 metalsmith 外掛進行渲染:
Metalsmith(process。cwd())
。metadata({
projectName: ‘專案名稱’,
projectDescription: ‘專案描述’,
// 這裡的資料實際上是上一步 ask 獲得的
})
。source(‘~/。feat-templates/feathers-template-default/templates/app’) // 模板檔案位置
。destination(process。cwd()) // 專案位置
。use(msPlugins。filterFiles(options。filters)) // 過濾檔案
。use(msPlugins。renderTemplateFiles()) // 渲染模板
。build((err) => {
if (err) {
log(`Metalsmith build error: ${err}`)
}
})
複製程式碼
推薦文章
- 朔爾茨訪華後,賓士CEO正面評價:絕對正確,疏遠中國簡直是不可思議
所以,儘管德國企業希望和中國深入合作,但考慮到國家安全利益,朔爾茨除了“擺姿態”之外,其實很難有更大的動作,畢竟歐盟還不是一個國家,也不是由德國一家說了算,諸如捷克、立陶宛等國,在美國的支援下,就能嚴重惡化中歐關係,這些也都不是德國可以“力...
- 美術統考、校考如何選擇,什麼樣的美術生適合衝校考?
美術統考和校考的區別簡介:美術統考是每個美術藝考生都必須參加的考試,由各省統一組織,不參加統考就不能進行藝術類志願填報了...
- 亞馬遜(AMZN.US)股價較階段性低點飆升超40%! 現在“上車”來得及嗎?
目標股價方面,Seeking Alpha彙總的華爾街分析師平均目標價為174...