最近有在折騰本身的腳手架,沒搞過這玩意,查閱了大量文章,有所收穫,因而有了這篇記錄。但願對一些童鞋有所幫助。前端
要了解前端腳手架的原理,能夠拿咱們最熟悉的 vue-cli
這個腳手架來講說,使用過的童鞋都知道使用腳手架建立 vue 項目時,須要使用命令:vue
vue create 項目名
複製代碼
你是否想過這句命令的原理呢,當這句命令執行時,它在背後到底進行了怎樣的操做呢?node
當咱們在電腦上安裝完 vue-cli
後,就可使用一個 vue
的命令。在使用命令前,咱們能夠先查看一下幫助信息,在終端中輸入 vue --help
命令就能夠看到一些幫助信息,以下:linux
$ vue --help
Usage: vue <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
create [options] <app-name> create a new project powered by vue-cli-service
add [options] <plugin> [pluginOptions] install a plugin and invoke its generator in an already created project
invoke [options] <plugin> [pluginOptions] invoke the generator of a plugin in an already created project
inspect [options] [paths...] inspect the webpack config in a project with vue-cli-service
serve [options] [entry] serve a .js or .vue file in development mode with zero config
build [options] [entry] build a .js or .vue file in production mode with zero config
ui [options] start and open the vue-cli ui
init [options] <template> <app-name> generate a project from a remote template (legacy API, requires @vue/cli-init)
config [options] [value] inspect and modify the config
outdated [options] (experimental) check for outdated vue cli service / plugins
upgrade [options] [plugin-name] (experimental) upgrade vue cli service / plugins
migrate [options] [plugin-name] (experimental) run migrator for an already-installed cli plugin
info print debugging information about your environment
Run vue <command> --help for detailed usage of given command.
複製代碼
從輸出信息上能夠看到 vue
是主命令,當咱們須要使用腳手架的一些操做命令時,須要以vue <command> [options]
這樣的方式去調用, 全部的操做都是基於這個主命令拓展的,好比 --help
,它屬於 Options
,能夠輸出幫助文檔, 又好比 create
命令,他屬於 Commands
,它也有本身的使用格式,可使用命令 vue create --help
查看,以下:webpack
$ vue create --help
Usage: create [options] <app-name>
create a new project powered by vue-cli-service
Options:
-p, --preset <presetName> Skip prompts and use saved or remote preset
-d, --default Skip prompts and use default preset
-i, --inlinePreset <json> Skip prompts and use inline JSON string as preset
-m, --packageManager <command> Use specified npm client when installing dependencies
-r, --registry <url> Use specified npm registry when installing dependencies (only for npm)
-g, --git [message] Force git initialization with initial commit message
-n, --no-git Skip git initialization
-f, --force Overwrite target directory if it exists
--merge Merge target directory if it exists
-c, --clone Use git clone when fetching remote preset
-x, --proxy <proxyUrl> Use specified proxy when creating project
-b, --bare Scaffold project without beginner instructions
--skipGetStarted Skip displaying "Get started" instructions
-h, --help output usage information
複製代碼
能夠看到,即便是一個子命令,它的使用參數也是不少的,由此也說明了 vue-cli
腳手架在背後作了不少工做,可是咱們也沒必要驚慌於腳手架的複雜,任何複雜的東西都不是一步到位的,咱們能夠先看看最基礎的 vue
命令是怎麼實現的。git
在終端能夠直接執行 vue
命令,說明它是一個全局命令,在個人 mac 電腦上,可使用 which vue
命令來查找它的位置:github
$ which vue
/usr/local/bin/vue
複製代碼
/usr/local/bin/
目錄存放了不少全局命令變量,包括 vue
,咱們能夠在這個目錄下使用 ll vue
來看下 vue
命令的詳情:web
$ ll vue
lrwxr-xr-x 1 wangjian admin 39B 6 2 14:17 vue -> ../lib/node_modules/@vue/cli/bin/vue.js
複製代碼
能夠看到,vue
實際上是一個軟連接,它指向的地址是../lib/node_modules/@vue/cli/bin/vue.js
,這個地址實際上是 npm
全局安裝的包的存放地址。還記得咱們安裝腳手架時的包名就是 @vue/cli
嗎,它在全局註冊的 vue
命令其實就是指向這個包文件下的 bin/vue.js
文件。如今咱們明白了使用命令 vue
時,執行的程序是 vue.js
。其實就是:vue-cli
vue -V
// 等同於下面
/usr/local/lib/node_modules/@vue/cli/bin/vue.js -V
複製代碼
/usr/local/lib/node_modules/ 是我電腦上 npm 全局安裝包的存放位置shell
那麼問題來了,vue.js
是怎麼運行起來的?js 文件是不能直接在終端中執行的。
js 文件主要有兩種執行方式,一個是在 web 頁面中執行,另外一個就是使用 node 執行。很顯然上述 vue.js
文件的執行應該是屬於第二種,即運行在 node 環境中。那麼是在哪裏執行的呢,咱們並無找到相似於 node. vue.js
這樣的執行入口。
其實答案就在 vue.js
文件自己。當咱們打開這個文件就能夠看到文件開頭第一行寫着:
#!/usr/bin/env node
複製代碼
這句代碼的做用就是會在所處電腦系統上自動查找 node
變量,而後使用 node
來執行當前腳本文件。意思就是說當 js 文件的頭部加上這一句後,就可使用 ./
的形式來執行這個文件。能夠測試一下,寫一個簡單的 js 腳本,就叫 test.js
吧:
#!/usr/bin/env node
console.log("hello world")
複製代碼
注意,若是是 mac 系統或者 linux 系統,須要給這個文件賦權 chmod 755 test.js
,而後就能夠執行了,以下:
可以在終端直接調用 js 文件是開發腳手架的一個關鍵知識點,下面咱們就嘗試作一個簡單的腳手架。
咱們能夠嘗試搭建一個簡單的腳手架雛形,它的功能沒必要完備,只要咱們理解了開發流程就能夠本身拓展。
隨便找一個目錄建立項目,而後 npm 初始化:
mkdir my-cli
cd my-cli
npm init -y
複製代碼
而後在項目目錄中新建一個入口文件 index.js
(名字隨意),在文件中隨意寫上一些 js 語句,記得首行要加上 #!/usr/bin/env node
,像這樣:
#! /usr/bin/env node
console.log("This is my cli tool")
複製代碼
此時項目結構很簡單:
my-cli
├── index.js
└── package.json
複製代碼
咱們還須要在 package.json
文件中配置 bin
選項,它是配置各個內部命令對應的可執行文件的位置,修改以下:
{
"name": "my-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"hi": "index.js" // 此處添加 bin 命令
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
複製代碼
bin
能夠配置成一個對象,每個 key 名都會被註冊成一個命令名,這裏我配置成 hi
命令。說到這裏引出一個問題,你知道爲何咱們安裝腳手架時安裝的包是 @vue/cli
,但最後執行的倒是 vue
命令嗎?就是由於 vue-cli 腳手架在註冊 bin 命令時,key 名爲 vue
。
此時我但願在終端輸入 hi
時,就能夠執行 index.js
文件,當咱們把這個項目發佈到 npm 上,而後本地全局安裝後能夠實現這樣的功能,但咱們本地調試有一個更簡單的方法,就是使用 npm link
,它能夠將項目連接到全局,達到等同於全局安裝的效果。在項目中執行 npm link
,以下:
mac 電腦須要加上 sudo 執行。此時咱們查看下全局環境下是否有 hi
命令:
有了,再使用 ll /usr/local/bin/hi
查看下詳細信息:
圖中能夠看到 my-cli
項目已經出如今 npm 全局安裝路徑中,hi
命令指向的正是項目中的 index.js
文件。來驗證下 hi
命令:
hi
命令生效了。而且因爲使用的是 npm link
方式,因此當你修改 index.js
文件時,是能夠同步更新 hi
命令執行結果的。到這裏,腳手架的第一步已經完成,接下來就是拓展命令操做而已,其實本質上就是在 index.js
文件中編寫 nodejs 代碼。
hi init
命令咱們能夠嘗試拓展一個 init
命令,這裏只寫思路,不涉及特別複雜的 init 內容。
當咱們在終端輸入 hi init
,但願可以識別到 init
參數,並執行對應邏輯。在 nodejs 中,可使用 process
模塊中的 argv
參數獲取命令行參數,將 my-cli
項目中的 index.js
文件改成以下內容:
#! /usr/bin/env node
const process = require('process')
console.log('argv=>>',process.argv)
複製代碼
而後在終端執行 hi init
:
能夠看到 process.argv
數組的第三項開始就是咱們在 hi
命令後輸入的參數,這樣就好辦了,咱們再改下代碼:
#! /usr/bin/env node
const process = require('process')
if(process.argv[2] === 'init'){
init()
}
function init(){
console.log('start init')
}
複製代碼
此時在終端執行 hi init
:
init
命令生效,實際開發中就能夠寫入對應邏輯,這裏再也不擴展說明了。
真實腳手架開發中,須要定義的操做命令可能會有不少,就如 vue-cli
那樣,此時須要藉助插件來解析參數,若是不用插件,就須要本身一個個來判斷所輸入的參數,影響效率且容易出錯。這裏推薦 commander 插件,具體使用方法請參考文檔,我這裏列舉一個使用插件的小例子:
#! /usr/bin/env node
const process = require('process')
const {program} = require('commander')
program
.version('0.0.1')
.option('-i, --init','用來初始化項目的')
.option('-r, --remove','用來刪除的')
.parse(process.argv)
const options = program.opts()
if(options.init){
console.log('正在執行初始化操做');
}
if(options.remove){
console.log('正在執行刪除操做');
}
複製代碼
此時在終端輸入對應命令的效果:
是否是像那麼回事了。
以上就是一個前端腳手架開發的基本原理,瞭解了這些基礎,就能夠慢慢開發一個本身的腳手架了。固然開發一個能用於生產的腳手架不是像寫 demo 那樣簡單的,須要學習的還有不少。以後有時間我會繼續記錄開發腳手架的相關文章,共同窗習!