前端腳手架開發原理

最近有在折騰本身的腳手架,沒搞過這玩意,查閱了大量文章,有所收穫,因而有了這篇記錄。但願對一些童鞋有所幫助。前端

瞭解 vue-cli 基本原理

要了解前端腳手架的原理,能夠拿咱們最熟悉的 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 命令的真身

在終端能夠直接執行 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 文件怎麼直接運行在終端

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,而後就能夠執行了,以下:

image.png

可以在終端直接調用 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,以下:

image.png

mac 電腦須要加上 sudo 執行。此時咱們查看下全局環境下是否有 hi 命令:

image.png

有了,再使用 ll /usr/local/bin/hi 查看下詳細信息:

image.png

圖中能夠看到 my-cli 項目已經出如今 npm 全局安裝路徑中,hi 命令指向的正是項目中的 index.js 文件。來驗證下 hi 命令:

image.png

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

image.png

能夠看到 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

image.png

init 命令生效,實際開發中就能夠寫入對應邏輯,這裏再也不擴展說明了。

使用 commanderjs 插件來開發

真實腳手架開發中,須要定義的操做命令可能會有不少,就如 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('正在執行刪除操做');
}
複製代碼

此時在終端輸入對應命令的效果:

image.png

image.png

image.png

image.png

是否是像那麼回事了。

結束

以上就是一個前端腳手架開發的基本原理,瞭解了這些基礎,就能夠慢慢開發一個本身的腳手架了。固然開發一個能用於生產的腳手架不是像寫 demo 那樣簡單的,須要學習的還有不少。以後有時間我會繼續記錄開發腳手架的相關文章,共同窗習!

相關文章
相關標籤/搜索