從零開始搭建本身的前端腳手架(一)

(如下閱讀將花費10分鐘)javascript

前言

平常開發中,咱們都只專一在業務上的開發,拿起一套開箱即用的模板項目就直接開搞了,不知道你們有沒有思考過,平時咱們使用的腳手架裏面到底作了什麼,而且若是是本身來搭一套腳手架,應該怎麼去搭呢? css

在本博客中,將記錄做者搭建腳手架的過程,總體將分爲兩個部分,第一部分是cli的搭建,第二部分是模板項目的搭建;java

cli的搭建

在搭建腳手架cli以前,咱們首先思考一下,一個cli,須要什麼能力呢?答案是初始化能力,那麼初始化的功能須要怎樣去實現呢,這裏,咱們先梳理一下思路。node

思路

咱們在使用其餘cli時,會發現,它們有問詢的功能,好比詢問項目名稱,項目描述等;而後還能夠選擇模板項目將要使用什麼css預處理器等的問題;因此,cli首先就要具有問詢功能,用以獲取定製化信息;
問詢結束後,咱們的cli會得到即將建立的項目的基本信息,接下來咱們就須要以一個模板項目做爲模板去建立,模板項目將會在第二部分講解,這裏一筆帶過~;OK,既然有了模板項目,cli就須要下載這個項目,而後複製這個項目,同時將前面問詢所得的定製化信息寫入項目配置中,因此cli還須要有下載複製,寫入模板功能;
到此,一個模板項目基本已經建立成功了,那後面咱們還但願腳手架能夠幫忙進行git初始化以及安裝依賴的功能,因此最終,還須要添加git初始化安裝依賴的功能;
總結一下,實現一個cli的初始化功能咱們須要有以下能力:
問詢 ==> 下載模板 ==> 複製,寫入模板 ==> git初始化 ==> 安裝依賴git

準備

好了,爲了實現上述思路,cli將會引入以下依賴去實現對應的功能:github

const program = require('commander');  // commander負責讀取命令
const inquirer = require('inquirer');   // inquirer負責問詢
const download = require('download-git-repo');   // download-git-repo負責下載對應模板項目的git倉庫
const fse = require('fs-extra');   // fs-extra負責文件的複製
const memFs = require('mem-fs');
const editor = require('mem-fs-editor');   // mem-fs-editor負責模板的複製以及嵌入模板字符串,它須要依賴mem-fs
const { exec } = require('child_process');   // child_process負責執行命令行

固然除了上述必須的依賴外,爲了更好的交互體驗,還引入了以下依賴:shell

const chalk = require('chalk');   // 改變命令行輸出樣式
const ora = require('ora');   // 一個優雅地命令行交互spinner

至此,準備的工做已經完畢,下面,就開始一步步來編寫cli了npm

cli的編寫

萬事開頭難,有了思路後,實現思路纔是真正的開始,下面將介紹如何組織cli項目json

項目組織

首先看一下,工程的組織數組

├── bin                   
|   ├── zero
|   ├── zero-init 
├── src                   
|   ├── constants.js     
|   ├── project.js         
|   └── utils.js              
├── .gitignore                   
├── .npmrc
├── README.md
└── package.json

第一步,解析命令

bin/zero中引入commander,而且聲明init命令,commander會在同級目錄中尋找zero-init文件

const program = require('commander');

program
  .usage('<command> [options]')
  .command('init [name]', 'init a project')
  .parse(process.argv);

第二步,問詢

有了第一步的解析後,咱們能夠在zero-init文件中編寫建立邏輯,這裏,咱們再抽象project文件到src目錄底下,以便更好地分離職責,bin文件只負責解析命令就好啦~
下面就看看project文件中,如何實現問詢

inquirer.prompt([{
      type: 'input',
      name: 'projectName',
      message: '請輸入項目名:',
      validate(input) {
        if (!input) {
          return '項目名不能爲空';
        }
        if (fse.existsSync(input)) {
          return '當前目錄已存在同名項目,請更換項目名';
        }
        return true;
      }
    }]);

inquirer提供prompt函數來實現問詢,其參數爲數組,問詢順序將按照數組的順序來進行;

第三步,下載倉庫

在問詢結束後,咱們基本知道須要建立的項目的名稱、描述等信息了,下一步,就是下載倉庫了,下面咱們來看看核心代碼:

const downloadPath = path.join(projectPath, '__download__');
download(TEMPLATE_GIT_REPO, downloadPath, { clone: true }, (err) => {
  // 拷貝
  // 此處省略若干代碼
  // 拷貝完成後刪除臨時文件
  fse.remove(downloadPath);
}

這裏須要注意的是,模板工程所在的倉庫,即TEMPLATE_GIT_REPO,最好是public的

第四步,複製文件,將信息寫入模板

下載倉庫只是把模板工程存放在一個臨時文件夾內,真正的工程文件須要等信息寫入模板後再生成;
這裏須要注意的是,模板寫入時遵循ejs規範

const memFs = require('mem-fs');
const editor = require('mem-fs-editor');

// 這裏須要mem-fs進行內存優化
const store = memFs.create();
this.memFsEditor = editor.create(store);

// 這裏source表示源文件,dest表示目標文件,data表示須要寫入的數據
this.memFsEditor.copyTpl(
  source,
  dest,
  data
);

在調用copyTpl時,data字段中的key-value將被寫入到模板中,舉個栗子:

// 模板文件中,字段是這樣的
{
  name: "<%= projectName %>"
}

// data就要這樣去寫
{
  projectName: 'this is your project name'
}

第五步,進行git init和npm install

這兩步的核心都是使用nodejs提供的child_process中exec方法去執行命令來實現,exec能幫助咱們在命令行中執行shell命令,exec函數有對應的回調方法來讓咱們判斷命令執行是否成功,核心代碼以下:

exec('npm install', (error, stdout, stderr) => {
  if (error) {
    // 出錯了
  } else {
    // 成功了
  }
})

是否是很簡單呢,想了解更多的操做能夠搜索官方文檔查看哈,這裏就不詳解了。

調試和發佈

到這裏,咱們的cli已經實現的差很少了,這裏咱們總結一下如何測試和發佈吧

npm link  // 本地調試
npm publish  // 發佈

經過npm link命令,便可以在命令行工具測試你的cli了,注意在package.jsonbin字段中定義好入口命令和文件

// package.json
{
  "bin": {
    "zero": "bin/zero"
  }
}

小結

OK,文章主要總結了編寫一個腳手架中init功能的思路以及一些實現的方法,具體一些細節仍有優化的空間,歡迎你們討論!
文章的具體代碼能夠參考個人倉庫zero-cli

相關文章
相關標籤/搜索