1. 程式人生 > 其它 >手摸手教你搭個腳手架

手摸手教你搭個腳手架

工欲善其事,必先利其器。

目錄

  1. 腳手架
  2. 全域性命令
  3. 互動式輸入
  4. 拉取遠端倉庫程式碼
  5. 優化
  6. 總結

1、腳手架

今天工地的磚有點燙,我抬頭望了望天,思考了半分鐘,決定為了以後搬磚方便,先搭個腳手架:

不好意思,拿錯了,是這個:

相信很多小夥伴都用過vue-cli、create-react-app或angular-cli之類的腳手架,一個命令列就能快速搭起專案框架,告別刀耕火種的複製貼上,分分鐘解放生產力。

作為一個有追求的切圖仔,這種摸魚神器,必須立馬安排。

好了,那麼問題來了:什麼是腳手架?

從表現形式上來看,腳手架主要有以下幾個特點:

  1. 一個能全域性執行的命令;
  2. 能實現互動式輸入,比如輸入專案名稱,選擇配置項等;
  3. 能自動拉取github或gitlab遠端倉庫的程式碼。

當然高階的腳手架肯定不止這麼點功能,不過我們先從最簡單的實現起。

2、全域性命令

2.1 基本原理

先拆包觀察一下,類似vue-cli這類的全域性命令是如何實現的。
首先找到vue-cli的安裝路徑,全域性安裝可以通過npm config get prefix找到路徑,比如我的windows系統就是
C:\Users\使用者名稱\AppData\Roaming\npm:

可以看到這個目錄下有很多與全域性命令同名的檔案,以及一個node_modules資料夾。我們全域性安裝的依賴包就在node_modules裡。找到@vue\cli,這個就是vue-cli

的原始碼包。

vue-cli原始碼包中的package.json有這樣一個關鍵配置:

  "bin": {
    "vue": "bin/vue.js"
  },

這個bin/vue.js指令碼必須以#!/usr/bin/env node開頭,

當npm全域性安裝時,就會根據這個配置,生成對應的同名可執行檔案。

而當我們執行vue時,系統就會以node程式來執行vue.js。

npm官方文件中提供了npm link命令,用以讓使用者將自定義的腳手架生成一個全域性命令。

npm link主要做了兩步操作:

  • 一是在npm全域性安裝路徑下的node_modules資料夾裡生成一個連結檔案,這個連結檔案指向執行該命令的資料夾,也就是我們的腳手架原始碼資料夾;
  • 二是在npm全域性安裝路徑下生成與配置bin裡同名的可執行檔案。

所以,通過npm link,我們就可以生成一個node全域性命令

npm link官方文件:https://docs.npmjs.com/cli/v6/commands/npm-link。

2.2 全域性命令的實現

話不多說,先來擼一個全域性命令。

1)執行npm init初始化一個package.json

2)在package.json里加上bin配置:

  "bin": {
    "test-cli": "./test.js"
  },

3)新增對應的執行指令碼檔案test.js

#! /usr/bin/env node
console.log("Hello! My CLI!");

4)在package.json同級目錄下執行npm link

5)在控制檯執行一下我們定義的全域性命令test-cli,看到輸出結果:

完成!

3、互動式輸入

通過上述操作,使用者已經可以通過全域性命令執行到我們的test.js檔案,剩下的功能我們就可以在js裡去自由發揮了。

Node社群有著數量龐大的第三方模組,藉由這些模組我們可以快速開發實現想要的功能。

inquirier就是其中之一,目前在github上有14.5k的star,它的目標就是“致力於成為一個易於嵌入且美觀的命令列工具”。顧名思義,通過inquirier模組,我們可以在命令列中實現與使用者的輸入互動。

我們需要知道使用者要建立的專案名稱是什麼,以及使用者想要下載哪個遠端倉庫的程式碼:

const inquirer = require("inquirer");

inquirer
    .prompt([
        {
            type: "input",
            name: "project",
            message: "專案名稱",
        },
        {
            type: "list",
            name: "tpl",
            message: "請選擇模板",
            choices: ["vue", "react"],
        }
    ])
    .then((res) => {
        console.log(res);
        const { project, tpl } = res;
        // project就是使用者輸入的專案名稱
        // tpl就是使用者選擇的模板
    });

這段程式碼就是為使用者提供了一個input輸入選項,來輸入專案名稱,以及一個list列表選項,來選擇要下載的模板(之後我們再根據這個模板名稱去對應的倉庫地址進行下載)。

inquirer還有更多的選項功能,感興趣的小夥伴可以去官方文件上自由探索:https://github.com/SBoudrias/Inquirer.js。

4、拉取遠端倉庫程式碼

現在我們已經知道使用者要下載的是哪個模板程式碼,也知道這些模板程式碼對應的下載地址:

const stores = [
    {
        name: "vue",
        url: "https://github.com/vuejs/vue.git"
    },
    {
        name: "react",
        url: "https://github.com/facebook/react.git"
    }
]

拉取github或者gitlab遠端倉庫程式碼的第三方模組也有好幾個,我選用的是nodegit,這個專案在github上目前有4.9k的star,用起來也很簡單:

const Git = require("nodegit");

/** 克隆遠端倉庫程式碼 */
// url: 原始碼倉庫地址; path: 要下載的目標路徑; cb: 下載結束後的回撥函式
const gitClone = (url, path, cb)=>{
    console.log("正在下載遠端倉庫程式碼...")
    console.log(url)
    Git.Clone(url, path)
       .then(function(res) {
            console.log("下載完成")
            cb(true)
        })
        .catch(function(err) { console.log("下載失敗"+err);cb(false) });
}

nodegit官網地址:
https://github.com/nodegit/nodegit

至此三個基本功能都已實現!

5、優化

深諳摸魚之道的我想了想,覺得還能更進一步,比如下載完原始碼,再幫我自動安裝下依賴包啦:

const process = require('child_process');

/** 安裝依賴包 */
const install = (path)=>{ // path是原始碼模板中package.json所在的路徑
    console.log("正在安裝依賴包...")
    const cmd = 'cd '+path+' && yarn';
    process.exec(cmd, function(error, stdout, stderr) {
        console.log(error);
        console.log(stdout);
        console.log(stderr);
        console.log("安裝完成")
    });
}

之前看到很多腳手架出場都自帶拉風的藝術字,作為一個有追求的切圖仔,這個必須安排!
這個也是用到了一個第三方模組figlethttps://github.com/patorjk/figlet.js):

const figlet = require('figlet');   
figlet('My CLI!', {horizontalLayout:"full"}, function(err, data) {
    if (err) {
        console.log('Something went wrong...');
        console.dir(err);
        return;
    }
    console.log(data)
    // do something...
});

很多腳手架還提供了引數配置、幫助資訊等功能,這個大多是通過commander模組實現的(https://github.com/tj/commander.js,20.7k star),這裡就不詳細展開了,我在demo專案裡也簡單實現了下,確實很強大很好用。

Demo已開源:https://github.com/youzouzou/testcli

這個demo只是簡單地實現了腳手架的基本功能,探索了一下幾個node模組的用法,還有很多需要優化的點。進一步學習的最好辦法就是去看那些成熟的腳手架原始碼,然後去模仿去實踐,再結合實際情況,摸索出最適合自己團隊的方案。

6、總結

工欲善其事,必先利其器。

實際上腳手架並不拘泥於上面的實現形式,凡是能提高效率的工具,在某種意義上都可以稱之為腳手架。

模仿只是最初級的階段,創新才是真正的開始。