vite不能選配方案?vite-creater強勢來襲!

2023-04-28 15:00:30

我正在參加「掘金·啟航計劃」

專案背景

vite出現之後,迅速帶走了一大波webpack的使用者,即使是對打包工具不熟悉的小白,也能很快感受到兩者的區別——vite快的多!

vite官方檔案第一句也是講述其名字的由來 Vite (法語意為 "快速的") ,其logo也與其名字一樣,處處都透露著一個字,那就是快!

但是習慣了vue-cli的同學(我),對於一個不能儲存模板策略的工具,是無法忍受的,它居然每次都需要我選擇模板、選擇用js還是ts,這是讓人無法忍受的。當然,vite官方還提供了社群維護的模板 即

這也是目前應用最多的,開發起來最容易的一套策略,在此直接回車,你會直接一鍵生成專案

如果你不喜歡,或者你需要更多的東西,可以點選 "點選進入自定義流程"

自定義選配

vite-creater會問詢一系列常用套件,包括:css前處理器、使用js還是ts、是否使用vue-router、使用vuex還是pinia

(vite-creater目前版本只支援 問詢vue常用包,如需其他框架,可以全選擇no,然後在自定義第三方包中新增你的框架需要的包即可)

最後,vite-creater會問你,是否需要其他的第三方包,比如我們需要使用 超好用的大屏自適應工具 vue-autofit ,我們就輸入vue-autofit

最後,可以選擇將此選配方案儲存,你可以選擇給它取一個名字,當然也可以不取,因為vite-creater會在預設方案列表中展示所有依賴名稱和使用的語言。

如果vite-creater要求你選擇框架,你可以根據自己的需要選擇你的框架:

當選擇完框架後,即可快速完成專案建立了!並且已經下載了你的自定義包。

使用儲存的選配方案

當你建立過一個方案並選擇了儲存後,工具會在下次使用時向你展示你儲存的選配方案,直接選擇即可一鍵生成專案,不用再重複選配過程。

vite-creater是怎麼實現的?

vite-creater是node編寫的。

呼叫Node編寫的cli程式,必須在node環境下才可以執行,因為node的cli實際上是由node代理執行的,即V8引擎去解析和執行的,當我們全域性安裝了一個node cli程式時,node會自動把其命令載入到環境變數中,當我們的JS程式碼被執行時,是node在與作業系統互動。所以,我們不需要了解它具體的執行原理,我們只需要知道JS怎麼寫就可以了。

開發所需的庫

"commander": "^10.0.1" 可以執行dos命令

"inquirer": "^9.2.0" 互動式輸出工具,提供問詢式命令列互動會用到

"configstore": "^6.0.0" 本地儲存,相當於是cookie或者localStorage,用來儲存使用者儲存的選配

開發步驟

使用npm 初始化專案
npm init

需要輸入專案基本資訊,此步驟會初始化一個標準的Npm包,並生成一個package.json ,

注意如果希望釋出到npm,應該先在npm官網檢視是否有相同或相似的包名,有的話是釋出不了的,需要取一個標新立異的包名,或者就需要帶上@userName/的字首

使用commander庫初始化命令
import { program } from 'commander';
program
  .version('1.0.0')
  .description(`vite-creater是一款用於快速建立vite專案的腳手架工具`);

program
  .command('init <projectName>')
  .description('使用vite-creater建立專案')
  .option('-p, --projectName <string>', 'project name')
  .action(async (initProjectName) => {
    await askForOptions(initProjectName) //這裡呼叫我們的自定義問詢函數
  });
program.parse(process.argv);

在node開發的cli中,可以使用async/await來阻塞程式,以等待步驟完成或者使用者輸入。

使用inquirer建立互動式問詢輸出
import inquirer from 'inquirer';

let preSetRules = [
    {
      name: 'selectRule',
      type: 'list',
      message: '選擇一個預設規則,或者進入自定義流程',
      choices: preSetRulesList, //這是一個陣列,僅可包含字串,如["item1","item2"]
    }
  ]
 let isPreSetRules = await inquirer.prompt(preSetRules)

當如上程式碼被執行時,將會輸出一個可以通過上下箭頭鍵選擇的列表(list)

使用configstore 儲存使用者的選配方案
import Configstore from 'configstore';
const conf = new Configstore('vite-creater');
conf.set("customRulesList", customRulesList); //新建或修改 引數:鍵名,資料
let customRulesList = conf.get('customRulesList');

其中customRulesList 可以是陣列或者物件,當然也可以是字串等,你可以把configstore 完全當作cookie來使用。

使用child_process建立子程序

這是一個node內建的庫,允許開發者建立一個子程序,並與子程序通訊

import { exec } from 'child_process';
function execCreateTs(command) {
  console.log('exec:', command);
  return new Promise((resolve, reject) => {
    const child = exec(command, (err, stdout, stderr) => {
      if (err) {
        console.log('err::: ', err);
        reject(err)
      }
    })
    child.stdout.on('data', async data => { //監聽子程序的輸出
      if (data.includes('Package name')) {
        process.stdout.write('\x1b[32m' + data + '\x1b[0m');
        child.stdin.write('\n');
      }
      if (data.includes('Vue')) {
        process.stdout.write('\x1b[2J\x1b[0f');
        process.stdout.write('\x1b[32m' + data + '\x1b[0m');
        clearAnimation()
        selectFramework(child)
      }
      if (data.includes('TypeScript')) {
        process.stdout.write('\x1b[32m' + data + '\x1b[0m');
        process.stdout.write('\x1b[2J\x1b[0f');
        child.stdin.write('\n');
        resolve(child.stdout)
      }
      if (data.includes('is not empty')) {
        // 退出程序
        console.log('\n\x1b[31m×\x1b[0m 目錄已存在');
        process.exit();
      }
    })
  })
}

上述程式碼由vite-creater建立ts專案為例,引數command即可以是任何dos命令。下面的if來查詢子程序的輸出中包含的字元,以此來確定子程序進行到哪一步了(我不知道這麼做是不是符合規範的,不過開發的時候我想到了這個辦法。)

細心的同學注意到了selectFramework()方法,這個方法是在子程序中出現了Vue字元時,我們判定vite進入了框架選擇步驟,於是我們將子程序的輸出展示到主程序中,也就是上面提到的框架選擇頁面

selectFramework方法程式碼如下:

async function waitUserPresskey() {
  // 返回一個promise物件
  return await new Promise((resolve, reject) => {
    process.stdin.setRawMode(true);
    process.stdin.resume();
    process.stdin.on('data', (key) => {
      key = key.toString('ascii'); //需要轉為ascii碼
      resolve(key)
    });
  });
}
async function selectFramework(child) {
  let input = await waitUserPresskey();
  // 如果按下ctrl+c,退出程序
  if (input === '\u0003') {
    process.exit();
  }
  child.stdin.write(input); // 向子程序轉發命令
}

當呼叫它時,程式會等待一個使用者輸入,然後直接轉發到子程序中,當子程序再次輸出其他資訊時,又會回到我們上面的監聽,於是,只要使用者還沒有選定框架,vite會一直處在框架選擇頁面,也就是還包含Vue字元,所以會再次進入selectFramework函數,這樣就完成了一個遞迴問詢,直到使用者選擇了框架。

全自動化流程實現

當完成上述步驟之後,我們已經可以看到vite+vue的專案已經建立完成了,這時就已經完成了vite官方工具所做的,一般來說,我們需要進入該資料夾,然後執行npm i ,然後安裝我們需要的包 npm i ....

根據上面的學習,我們理所當然的可以將這一步收納進自動化的範疇,只需執行兩條簡單的dos命令即可。

    await execNpmInstall('npm i')
    await execNpmInstall(installCommand)
function execNpmInstall(command) {
  console.log('exec:', command);
  return new Promise((resolve, reject) => {
    const child = exec(command, (err, stdout, stderr) => {
      if (err) {
        reject(err)
      }
    })
    child.stdout.on('data', async data => {
      // 當npm i 完成時
      if (data.includes('packages in')) {
        console.log('\n', data);
        resolve(child.stdout)
      }
    })
  })
}

如果我們需要安裝 之前使用者儲存的第三方庫,只需要使用使用conf.get('xxx');去獲取資料,然後傳入該函數即可。

完成

上面簡述了vite-creater的開發過程,至此,我們就可以整理所有的功能,打包釋出了,在本地登入自己的npm賬號後,使用npm publish命令即可釋出。

檢視 vite-creater 的 npm主頁

本專案已在 github開源 github原始碼