日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从零开发cli脚手架

發布時間:2024/1/1 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零开发cli脚手架 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是一篇篇幅很長的文章,分為四個部分,每個部分都可以單獨食用。

  • 使用npm發布插件包
  • 學習使用commander.js插件
  • 學習使用inquirer.js插件
  • 在vue-cli基礎上封裝一個cli

  • 1.使用npm發布插件包

    npm 為我們打開了連接整個 JavaScript 天才世界的一扇大門。它是世界上最大的軟件注冊表,每星期大約有 30 億次的下載量,包含超過 600000 個 包(package) (即,代碼模塊)。來自各大洲的開源軟件開發者使用 npm 互相分享和借鑒。包的結構使您能夠輕松跟蹤依賴項和版本。
    npm 由三個獨立的部分組成:?

  • 網站:是開發者查找包(package)、設置參數以及管理 npm 使用體驗的主要途徑。
  • 注冊表(registry):是一個巨大的數據庫,保存了每個包(package)的信息。
  • 命令行工具 (CLI):通過命令行或終端運行。開發者通過 CLI 與 npm 打交道。

  • 我們可以從npm上獲取大量優秀第三方包,當然我們也可以上傳自己的包。
    下面簡單介紹如何使用npm創建和發布自己的包。
    ?

    創建npm賬號

    要上傳我們自己的包,首先要有npm賬號,如果你沒有npm賬號,可以去[注冊頁](https://www.npmjs.com/signup)注冊并登錄自己的賬號。輸入用戶名、郵件、密碼即可創建。<br />在命令行輸入`npm login`,根據提示輸入賬號、密碼,即可在命令行登錄我們的賬號。后續可以通過此賬號上傳我們開發的npm包。<br />在命令行輸入`npm whoami`可以測試自己是否登錄成功,登錄成功則會打印出你的用戶名。<br />

    包的分類

    npm包分為public和private兩種類型。

    scopes

    在此之前,我們先了解這么一個概念——scopes(中文意思是作用域)。我們在注冊npm賬號和創建組織時,你將被授予一個與你的用戶或組織名稱匹配的范圍,即你獲得了一個適用范圍(scope),這個范圍是你的用戶名或者創建的組織名。你可以將此范圍用作相關包的命名空間。如你有一個package名叫mypackage,你的用戶名為myusername,則你可以把這個package放到你的域里`@myusername/mypackage ?`<br />作用:<br />避免與別人的包重名,發生沖突。<br />限制該包的訪問權限。<br />

    pubilc package

    作為npm用戶或組織成員,你可以創建和發布公共包,任何人都可以下載并在他們自己的項目中使用。?
    **unscoped:**的公共包存在于全球公共注冊表(http://registry.npmjs.org)的命名空間中,并且可以在package.json文件中僅以包的名稱來引用:package-name。
    **scoped:**的公共包屬于某個用戶或組織,在package.json文件中作為依賴關系包含時,必須在前面加上用戶或組織名稱。
    @username/package-name
    @org-name/package-name

    private package

    要使用私有軟件包,你必須使用npm 2.7.0或更高版本,并且有一個付費用戶或組織賬戶。
    通過npm私有包,你可以使用npm注冊表來托管只對你和選定的合作者可見的代碼,允許你在項目中管理和使用私有代碼與公共代碼。
    ?

    私有包總是有一個范圍,而范圍內的包默認為私有。


    用戶范圍的私有包只能由你和被你授予讀或讀/寫權限的合作者訪問。
    組織范圍的私有包只能由被授予讀或讀/寫權限的團隊訪問。
    ?

    創建package.json文件

    發布到注冊表的軟件包必須包含一個package.json文件。package.json文件會表明你的包名、版本、作用、列出你的項目所依賴的軟件包、關鍵詞等等。這份文件是你需要知道的關于package.json文件中需要的所有內容。它必須是實際的JSON,而不僅僅是一個JavaScript對象字面。所以package.json對npm包來說至關重要。我們使用動手創建demo來學習。
    打開命令行,在指定目錄創建一個demo項目
    mkdir zzmath
    cd zzmath
    創建package.json文件
    npm init,根據提示輸入你的包的信息。
    使用編輯器打開package.json文件。

    {"name": "zzmath","version": "1.0.0","description": "一些簡單的數據計算方法封裝","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": ["math","js"],"author": "","license": "ISC" }

    這就是創建package.json文件后會有的一些信息。
    name:包名
    version: 版本號
    description:包的描述
    scripts: 腳本
    main:指定包的入口文件
    keywords: 包的關鍵詞
    author: 作者
    license: ISC


    更多字段請參考官方介紹

    README.md

    為了幫助其他人在npm上找到你的軟件包,并在他們的項目中擁有使用你的代碼的良好體驗,建議在你的包的根目錄中包括一個README文件。你的 README 文件可以包括安裝、配置和使用你的軟件包中的代碼的說明,以及任何其他用戶可能會發現的有用信息。README文件將顯示在包頁面上。

    語義版本管理

    為了保持JavaScript生態系統的健康、可靠和安全,每當你對自己擁有的npm包進行重大更新時,建議在package.json文件中發布該包的新版本,并在其上標注更新的版本號,以遵循語義版本規范。遵循語義版本規范有助于依賴你的代碼的其他開發人員了解特定版本的變化程度,并在必要時調整自己的代碼。
    軟件包版本從1.0.0開始,遵循以下規則。
    語義版本控制的概念很簡單:所有的版本都有 3 個數字:x.y.z。?

    1. 第一個數字是主版本。1. 第二個數字是次版本。1. 第三個數字是補丁版本。


    當發布新的版本時:
    當進行不兼容的 API 更改時,則升級主版本。
    當以向后兼容的方式添加功能時,則升級次版本。
    當進行向后兼容的缺陷修復時,則升級補丁版本。


    npm 設置了一些規則,可用于在 package.json 文件中選擇要將軟件包更新到的版本(當運行 npm update 時)。規則使用了這些符號:
    ?

    ^: 只會執行不更改最左邊非零數字的更新。 如果寫入的是 ^0.13.0,則當運行 npm update 時,可以更新到 0.13.1、0.13.2 等,但不能更新到 0.14.0 或更高版本。 如果寫入的是 ^1.13.0,則當運行 npm update 時,可以更新到 1.13.1、1.14.0 等,但不能更新到 2.0.0 或更高版本。
    ~: 如果寫入的是 ?0.13.0,則當運行 npm update 時,會更新到補丁版本:即 0.13.1 可以,但 0.14.0 不可以。
    >: 接受高于指定版本的任何版本。
    >=: 接受等于或高于指定版本的任何版本。
    <=: 接受等于或低于指定版本的任何版本。
    <: 接受低于指定版本的任何版本。
    =: 接受確切的版本。
    -: 接受一定范圍的版本。例如:2.1.0 - 2.6.2。
    ||: 組合集合。例如 < 2.1 || > 2.6。
    可以合并其中的一些符號,例如 1.0.0 || >=1.1.0 <1.2.0,即使用 1.0.0 或從 1.1.0 開始但低于 1.2.0 的版本。
    還有其他的規則:
    無符號: 僅接受指定的特定版本(例如 1.2.1)。
    latest: 使用可用的最新版本。
    ?

    發布和更新包

    繼續使用前面的demo,我們來開發一個簡單的工具包,并發布到npm上。
    新建index.js文件,封裝兩個簡單的數學計算方法。

    //index.js /*** @description: 返回數組中最大數的兩倍* @param {Array} arr* @return {*}*/ function maxDouble(arr){return Math.max(...arr) * 2; }/*** @description: 返回數組中最小數的兩倍* @param {Array} arr* @return {*}*/ function minDouble(arr){return Math.min(...arr) * 2; }module.exports = {maxDouble,minDouble, };

    發布包

    在命令行執行npm publish,即可發布我們的第一個npm包
    ![截屏2021-06-29 下午10.03.55.png](https://img-blog.csdnimg.cn/img_convert/a108c78f0ca81bb5e3a02183d5218144.png#clientId=u17704fe2-6f8f-4&from=drop&id=u04d713f3&margin=[object Object]&name=截屏2021-06-29 下午10.03.55.png&originHeight=286&originWidth=492&originalType=binary&ratio=1&size=50906&status=done&style=none&taskId=uc00ff8c6-fca1-446f-ba4f-4e22bcf543e)


    到npm官網搜索即可找到我們剛發布的npm包![截屏2021-06-29 下午10.05.11.png](https://img-blog.csdnimg.cn/img_convert/743827477a7bdabc75ea7b8c1112ee62.png#clientId=u17704fe2-6f8f-4&from=drop&id=u0fb04cc4&margin=[object Object]&name=截屏2021-06-29 下午10.05.11.png&originHeight=265&originWidth=953&originalType=binary&ratio=1&size=25285&status=done&style=none&taskId=ue799787f-5f99-461c-9c63-c90c467fde9)


    使用發布的包


    新建一個test-demo小項目,并執行安裝命令,安裝我們的包
    ![截屏2021-06-29 下午10.08.33.png](https://img-blog.csdnimg.cn/img_convert/a2f01d217ec834be8385f859fde30b3d.png#clientId=u17704fe2-6f8f-4&from=drop&id=uc76d14c8&margin=[object Object]&name=截屏2021-06-29 下午10.08.33.png&originHeight=116&originWidth=459&originalType=binary&ratio=1&size=17184&status=done&style=none&taskId=u791a5f91-0d0e-435e-83e0-3c2980943c2)
    ![截屏2021-06-29 下午10.09.36.png](https://img-blog.csdnimg.cn/img_convert/2906c7c110a5a85b5fb7e69df313b212.png#clientId=u17704fe2-6f8f-4&from=drop&id=u0c132981&margin=[object Object]&name=截屏2021-06-29 下午10.09.36.png&originHeight=116&originWidth=234&originalType=binary&ratio=1&size=8833&status=done&style=none&taskId=u3eacad32-03a7-4686-a725-921a2494947)


    新建test.js,導入我們的包

    //test.js const zzmath = require('zzmath')console.log(zzmath.maxDouble([2, 10, 11])); console.log(zzmath.minDouble([2, 10, 11]));

    在控制臺 執行命令 node test.js,查看輸出。

    zhuxiaodong@zhuxiaodongdeMacBook-Pro test-demo % node test.js 22 4

    更新包

    前面說過,我們的包需要嚴格遵守語言版本管理,我們更新包的時候需要更新包的版本號。我們可以通過修改package.json文件里version來更改包的版本。或者通過執行命令
    npm version patch:更新補丁版本
    npm version major:更新主版本
    npm version minor:更新次版本
    ?

    我們修改一下我們的包,然后更改版本號,執行npm publish即可更新包。
    ![截屏2021-06-29 下午10.31.01.png](https://img-blog.csdnimg.cn/img_convert/e2bc9d3d6699dab4b48d3da82119b2f3.png#clientId=u17704fe2-6f8f-4&from=drop&id=u5450f32d&margin=[object Object]&name=截屏2021-06-29 下午10.31.01.png&originHeight=322&originWidth=543&originalType=binary&ratio=1&size=68714&status=done&style=none&taskId=ubdd24341-5ba1-41e3-9188-913177a9aef)![截屏2021-06-29 下午10.31.45.png](https://img-blog.csdnimg.cn/img_convert/056378d93c469d0dcc940e980fdf23d2.png#clientId=u17704fe2-6f8f-4&from=drop&id=u34a8a627&margin=[object Object]&name=截屏2021-06-29 下午10.31.45.png&originHeight=91&originWidth=439&originalType=binary&ratio=1&size=10243&status=done&style=none&taskId=u1e41ceb3-d634-400d-a03c-b3c6a6b4103)

    為軟件包添加dist-tags

    (dist-tags)是人類可讀的標簽,你可以用它來組織和標記你發布的軟件包的不同版本。dist-tags是對語義版本的補充。除了比語義版本號更容易讓人讀懂之外,標簽還允許發布者更有效地發布他們的軟件包。
    比如我們可以為包打上beta的標簽,告訴人們此包還處于beta測試中,請謹慎使用。
    :::warning
    :由于dist-tags與語義版本共享一個命名空間,因此要避免dist-tags與現有版本號發生沖突。建議避免使用以數字或字母 "v "開頭的dist-tag。
    :::


    使用命令npm publish --tag <tag> 為包打標簽。
    ?

    也可以使用以下命令為指定的版本打上標簽
    npm dist-tag add <package-name>@<version> [<tag>]
    ?

    我們為我們的包打上beta標簽,在打標簽之前需要先更新版本號。
    ![截屏2021-06-29 下午10.39.17.png](https://img-blog.csdnimg.cn/img_convert/c9f05a3a3d4d68f2d01a59c04462d1d6.png#clientId=u17704fe2-6f8f-4&from=drop&id=GAcSj&margin=[object Object]&name=截屏2021-06-29 下午10.39.17.png&originHeight=323&originWidth=500&originalType=binary&ratio=1&size=57244&status=done&style=none&taskId=u7bf35bd9-c947-42b5-aa2e-4f044a6caf1)
    ![截屏2021-06-29 下午10.41.57.png](https://img-blog.csdnimg.cn/img_convert/c1c31c45c2bb06ed04f0fb5713f0b13a.png#clientId=u17704fe2-6f8f-4&from=drop&height=527&id=otCvH&margin=[object Object]&name=截屏2021-06-29 下午10.41.57.png&originHeight=682&originWidth=795&originalType=binary&ratio=1&size=64108&status=done&style=none&taskId=ucd802bfc-5395-4d1e-bfca-6ac71cff6c0&width=614)
    使用npm發布插件包就講到這里,更多知識請查看官方文檔。

    2.commander.js的簡單使用

    commander.js是完整的 node.js 命令行解決方案。可以為你的腳手架工具定義執行命令、選項,處理傳入的參數以及自動生成幫助信息等。基本上是現在腳手架工具必裝的插件。

    安裝

    npm install commander
    ?

    聲明program變量

    #!/usr/bin/env node const { program } = require('commander');//orconst { Command } = require('commander'); const program = new Command();

    首先體驗一個小例子:

  • 新建一個項目文件夾easy-cli,進入文件夾,執行npm init根據提示一直回車,新建package.json文件。
  • 執行npm install commander安裝commander。
  • ![截屏2021-06-20 下午1.55.53.png](https://img-blog.csdnimg.cn/img_convert/94c7f35f39dd0cf468fb3e80775382ec.png#clientId=uf24dd6db-2d4e-4&from=drop&height=137&id=u8fc2ef31&margin=[object Object]&name=截屏2021-06-20 下午1.55.53.png&originHeight=448&originWidth=868&originalType=binary&ratio=2&size=74645&status=done&style=none&taskId=u9134fbec-eba8-45fd-bb87-b3a47fbb2c8&width=265)
    根據前兩部操作完成之后大概是這樣。

  • 在根目錄新建bin文件夾,在文件夾中新建easy.js文件。
  • 在package.json文件中設置命令名到本地文件名的映射。
  • "bin": {"easy": "bin/easy.js"},
  • 在easy文件開頭輸入#!/usr/bin/env node,指定easy.js的解釋程序是node。
  • 聲明program變量
  • #!/usr/bin/env node //easy.js const { Command } = require("commander"); const program = new Command();
  • 定義一個選項
  • //easy.js //..other codeprogram.option('-c, --cons', 'console hello world!')//解析 program.parse() const options = program.opts();if(options.cons) console.log("hello world!");

    測試我們的命令:
    ?

    在控制臺輸入npm i -g ../easy-cli 安裝我們的cli,mac用戶可能需要使用sudo權限sudo npm i -g ../easy-cli安裝。這樣就在全局和我們的cli項目文件建立了一個連接。
    使用npm list -g可以查看我們在全局安裝的所有包。

    zxd@zxddeMBP easy-cli % npm list -g /usr/local/lib ├── @vue/cli-init@4.2.2 ├── @vue/cli@4.5.9 ├── easy-cli@1.0.0 -> ./../../../Users/zxd/learnSpace/easy-cli ├── express-generator@4.16.1 ├── http-server@0.12.3 ├── nodemon@2.0.6 ├── npm@7.15.0 └── typescript@4.1.2

    可以看到我們的easy-cli已經安裝到了全局。
    只需建立一次連接,在后續修改cli后,不需要再次執行。
    ?

    在不需要調試的時候可以使用npm unlink -g easy-cli刪除連接。
    ?

    在命令行執行我們的命令以及選項 easy -c?
    輸出:
    hello world!

  • 定義一個命令
  • //easy.js //..other code program.option('-c, --cons', 'console hello world!')program.command("double").arguments("<number>").description("將輸入的參數x2").action((number) => {console.log(number*2);});program.parse() const options = program.opts();if(options.cons) console.log("hello world!");

    在命令行輸入 easy double 2執行這個命令
    輸出:
    4
    ?

    在命令行輸入 easy double 2 -c
    輸出:
    4
    hello world!


    大家可以嘗試執行以下命令,看看會打印什么

  • easy
  • easy double
  • easy -c double 2
  • 在沒定義命令和定義命令分別執行easy -c
  • ?

    帶著好奇和疑問,開始學習commander.js
    ?

    選項(option)

    option是我們能夠通過命令執行的選項。例如:在安裝npm包時,將包安裝在dev依賴里。
    npm i <package-name> -save-dev,--save-dev就是定義的選項。
    ?

    Commander 使用.option() 方法來定義選項,同時可以附加選項的簡介。每個選項可以定義一個短選項名稱(-后面接單個字符)和一個長選項名稱(–后面接一個或多個單詞),使用逗號、空格或|分隔。

    program.option('-s, --small', 'small pizza size') // 短名稱 長名稱 選項簡介

    選項參數

    有兩種最常用的選項,一類是 boolean 型選項,選項無需配置參數(上面體驗的那種),另一類選項則可以設置參數(使用尖括號聲明在該選項后,如–expect )。如果在命令行中不指定具體的選項及參數,則會被定義為undefined。帶參數的又分為必填參數和可選參數

    選項可以通過在Command對象上調用.opts()方法來獲取
    通過program.parse(arguments)方法處理參數,沒有被使用的選項會存放在program.args數組中。該方法的參數是可選的,默認值為process.argv。

    必填參數

    使用尖括號定義的參數是必填的。

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.option("-c, --cons <input>", "console input"); //參數program.parse()const options = program.opts();if(options.cons) console.log(options.cons);

    執行命令 easy -c "hey,how are you?"
    輸出:
    hey,how are you?
    ?

    執行命令 easy -c
    輸出:
    error: option '-c, --cons ’ argument missing
    ?

    可選參數

    使用方括號定義的參數是可選的。選項在不帶參數時可用作boolean選項,在帶有參數時則從參數中得到值。

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.option("-c, --cons [input]", "console input or console hello world!"); //參數program.parse();const options = program.opts();if (options.cons===true){console.log("hello world!"); }else{console.log(options.cons); }

    執行命令 easy -c "hey,how are you?"
    輸出:
    hey,how are you?
    ?

    執行命令 easy -c
    輸出:
    hello world!
    ?

    選項默認值

    可以通過在選項解釋后面添加第四個值來為選項添加默認值。
    :::warning
    必填選項的默認值只在輸入命令時 不輸入必填選項 時使用,如果輸入必填選項,但是沒有輸入必填選項的參數則會報錯。
    :::

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.option("-c, --cons [input]", "console input or console hello world!","Hola"); //可選選項 默認值 program.option("-ch, --cheese <type>","add the specified type of cheese","blue" );//必填選項 默認值program.parse(); const options = program.opts();console.log(options.cons); console.log(`cheese: ${program.opts().cheese}`);

    執行命令 easy -c "hey,how are you?"
    輸出:
    hey,how are you?
    cheese: blue
    ?

    執行命令 easy -c
    輸出:
    Hola
    cheese: blue
    ?

    執行命令 easy -c -ch
    輸出:
    error: option '-ch, --cheese ’ argument missing
    ?

    變長參數選項

    定義選項時,可以通過使用…來設置參數為可變長參數。在命令行中,用戶可以輸入多個參數,解析后會以數組形式存儲在對應屬性字段中。在輸入下一個選項前(-或–開頭),用戶輸入的指令均會被視作變長參數。與普通參數一樣的是,可以通過–標記當前命令的結束。

    #!/usr/bin/env node program.option('-n, --number <numbers...>', 'specify numbers').option('-l, --letter [letters...]', 'specify letters');program.parse();console.log('Options: ', program.opts()); console.log('Remaining arguments: ', program.args);

    執行命令 easy -n 1 2 3 --letter a b c
    輸出:

    Options: { number: [ '1', '2', '3' ], letter: [ 'a', 'b', 'c' ] } Remaining arguments: []

    執行命令 easy -n 1 2 3 --letter a b c -- ext
    輸出:

    Options: { number: [ '1', '2', '3' ], letter: [ 'a', 'b', 'c' ] } Remaining arguments: [ 'ext' ]

    版本選項

    version方法可以設置版本,其默認選項為-V和--version,設置了版本后,命令行會輸出當前的版本號。

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.version("0.0.1");//通常使用package.json文件里的version //program.version(require("../package.json").version)//可以在version方法里再傳遞一些參數(長選項名稱,描述信息),用法與option方法類似。 //program.version('0.0.1', '-v, --vers', 'output the current version');program.parse()

    執行命令:easy -V
    輸出:0.0.1

    幫助選項

    commander.js會自動生成幫助信息。默認幫助選項幫助信息是-h,--help。
    命令行執行easy -h
    輸出:

    Usage: easy [options]Options:-V, --version output the version number-h, --help display help for command

    我們也可以自定義幫助信息

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.option('-f, --foo', 'enable some foo');program.addHelpText('after', ` Example call:$ custom-help --help`);program.parse(process.argv);

    命令行執行easy -h
    輸出:

    Usage: easy [options]Options:-f, --foo enable some foo-h, --help display help for commandExample call:$ custom-help --help

    更多幫助信息的設置請參考官方文檔。
    ?

    命令(command)

    命令就是我們使用腳本執行的方法,如我們使用npm安裝包的時候使用 npm i <package-name> -s或者npm install <package-name> -s命令執行安裝方法,i 和 install就是方法名,i是方法的簡稱,install是方法的全稱,-s就是命令的選項。將包安裝在dependencies。
    ?

    通過.command()或.addCommand()可以配置命令,有兩種實現方式:

  • 為命令綁定處理函數
  • 將命令單獨寫成一個可執行文件(適用命令的邏輯過多的時候)

  • .command()的第一個參數可以配置命令名稱及命令參數,參數支持必選(尖括號表示)、可選(方括號表示)及變長參數(點號表示,如果使用,只能是最后一個參數)。跟選項類似。尖括號(例如)意味著必選,而方括號(例如[optional])則代表可選。可以向.description()方法傳遞第二個參數,從而在幫助中展示命令參數的信息。該參數是一個包含了 “命令參數名稱:命令參數描述” 鍵值對的對象。
    ?

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.command("clone <source> [destination]") //定義命令名稱clone 必填參數source 可選參數destination.description("clone a repository into a newly created directory") //命令的描述.action((source, destination) => {//命令處理函數console.log(`repository has been cloned from ${source} to ${destination}`);});program.parse(process.argv);

    命令行執行easy clone a b
    輸出:

    repository has been cloned from a to b

    也可以將命令寫成單獨的可執行文件
    ?

    當.command()帶有描述參數時,就意味著使用獨立的可執行文件作為子命令。 Commander 將會嘗試在入口腳本(例如 ./examples/pm)的目錄中搜索program-command形式的可執行文件,例如easy-start, pm-install。通過配置選項executableFile可以自定義名字。

    #!/usr/bin/env node //easy.js const { Command } = require("commander"); const program = new Command();program .command("start", "每隔1秒輸出當前時間秒數")// 需要有 easy-start.js可執行文件 .command("start2","每隔2秒輸出當前時間秒數", { executableFile: 'milliseconds' })//需要有milliseconds.js 可執行文件program.parse(process.argv);


    新建easy-start.js文件

    #!/usr/bin/env nodesetInterval(() => {console.log(new Date().getSeconds()); }, 1000);

    新建milliseconds.js文件

    #!/usr/bin/env nodesetInterval(() => {console.log(new Date().getSeconds()); }, 2000);


    分別執行命令:easy start、easy start2,查看輸出。

    變長參數

    在參數名后加上...來聲明可變參數,且只有最后一個參數支持這種用法。

    program.command('rmdir <dirs...>').action(function (dirs) {dirs.forEach((dir) => {console.log('rmdir %s', dir);});});

    命令行輸入easy rmdir 1 2 34 45
    輸出:
    rmdir 1
    rmdir 2
    rmdir 34
    rmdir 45
    ?

    單獨的可執行文件接收參數

    #!/usr/bin/env node //easy.js const { Command } = require("commander"); const program = new Command();program .command("start <length> <number...>", "遍歷number")// 需要有 easy-start.js可執行文件program.parse(process.argv); #!/usr/bin/env node //easy-start.js const { Command } = require("commander"); const program = new Command();program.parse();console.log(program.args)

    命令行輸入easy start 5 12345 4 23
    輸出:[ ‘5’, ‘12345’, ‘4’, ‘23’ ]
    ?

    需要通過制定參數位置來取參數。 "5"是length,剩下的都是number。
    ?

    監聽

    監聽命令和選項可以執行自定義函數。

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.option("-c, --cons", "console hello world!");program.command("clone <source> [destination]")//定義命令名稱clone 必填參數source 可選參數destination.description("clone a repository into a newly created directory") //命令的描述.action((source, destination) => {//命令處理函數console.log(`repository has been cloned from ${source} to ${destination}`);});program.on("option:cons", function () {console.log("輸入選項 --cons") });program.on("command:*", function (operands) {console.error(`error: unknown command '${operands[0]}'`);const availableCommands = program.commands.map((cmd) => cmd.name());console.log("availableCommands:", availableCommands); });program.parse(); const options = program.opts();if (options.cons) console.log("hello world!");

    命令行輸入easy clone a b -c
    輸出:

    輸入選項 --cons repository has been cloned from a to b hello world!

    命令行輸入easy pull a b -c
    輸出:

    輸入選項 --cons error: unknown command 'pull' availableCommands: [ 'clone' ] hello world!

    示例

    const { Command } = require('commander'); const program = new Command();program.version('0.0.1').option('-c, --config <path>', 'set config path', './deploy.conf');program.command('setup [env]').description('run setup commands for all envs').option('-s, --setup_mode <mode>', 'Which setup mode to use', 'normal')//命令的選項.action((env, options) => {env = env || 'all';console.log('read config from %s', program.opts().config);console.log('setup for %s env(s) with %s mode', env, options.setup_mode);});program.command('exec <script>').alias('ex')//定義別名.description('execute the given remote cmd').option('-e, --exec_mode <mode>', 'Which exec mode to use', 'fast').action((script, options) => {console.log('read config from %s', program.opts().config);console.log('exec "%s" using %s mode and config %s', script, options.exec_mode, program.opts().config);}).addHelpText('after', ` Examples:$ deploy exec sequential$ deploy exec async`);program.parse(process.argv);


    更多知識請前往官方文檔。

    3.inquirer.js的使用

    inquirer.js是一個命令行交互式工具,通過設置問題、選項,在執行命令時在控制臺展示這些問題,并在用戶做出回答后接收答案。

    安裝

    npm install inquirer

    Demo小體驗

    #!/usr/bin/env node const inquirer = require("inquirer");const requireLetterAndNumber = (value) => {if (/\w/.test(value) && /\d/.test(value)) {return true;}return "Password need to have at least a letter and a number"; };const questions = [{type: "expand",message: "Conflict on `file.js`",name: "q_1",choices: [{key: "y",name: "Overwrite",value: "overwrite",},{key: "a",name: "Overwrite this one and all next",value: "overwrite_all",},{key: "d",name: "Show diff",value: "diff",},new inquirer.Separator("."),{key: "x",name: "Abort",value: "abort",},],},{type: "input",name: "q_2",message: "Question with filtering and validating Text",validate: async () => {await new Promise((r) => setTimeout(r, 3000));return true;},filter: async (answer) => {await new Promise((r) => setTimeout(r, 3000));return `filtered${answer}`;},filteringText: "Filtering your answer...",validatingText: "validating what you wrote",},{type: "input",name: "q_3",message: "Question without filtering and validating Text",validate: async () => {await new Promise((r) => setTimeout(r, 3000));return true;},filter: async (answer) => {await new Promise((r) => setTimeout(r, 3000));return `filtered${answer}`;},},{type: "input",name: "q_4",message: "what‘s your last name",default() {return "Doe";},},{type: "list",name: "q_5",message: "What do you want to do?",choices: ["Order a pizza","Make a reservation",new inquirer.Separator(),"Ask for opening hours",{name: "Contact support",disabled: "Unavailable at this time",},"Talk to the receptionist",],},{type: "list",name: "q_6",message: "What size do you need?",choices: ["Jumbo", "Large", "Standard", "Medium"],filter(val) {return val.toLowerCase();},},{type: "password",name: "q_9",message: "Enter a password",validate: requireLetterAndNumber,},{type: "password",name: "q_10",message: "Enter a masked password",mask: "*",validate: requireLetterAndNumber,},{type: "confirm",name: "q_11",message: "password is ok?",default: true,},{type: "input",name: "q_12",message: "How many do you need?",validate(value) {const valid = !isNaN(parseFloat(value));return valid || "Please enter a number";},filter: Number,},{type: "rawlist",name: "q_13",message: "What do you want to do?",choices: ["Order a pizza","Make a reservation",new inquirer.Separator(),"Ask opening hours","Talk to the receptionist",],}, ];inquirer.prompt(questions).then((answers) => {console.log(JSON.stringify(answers, null, " ")); });

    在控制臺執行 node demo.js 查看效果。
    ?

    通過分析上面的代碼和控制臺的交互效果,我們看到我們定義了一個對象questions數組,每一個對象有type``name``message,有些有validate``dafault``filter``choices等,將這個questions數組作為參數傳給inquirer.prompt()方法,返回一個promise,可以得到用戶在控制臺輸入的答案。

    Methods

    inquirer.prompt(questions, answers) -> promise
    ?

    通過inquirer.prompt()方法啟動我們的提示。
    questions:(數組)包含問題對象(使用反應式接口,你也可以傳遞一個Rx.Observable實例
    answers (對象)包含已經回答的問題的值。默認為{}。
    返回一個Promise
    ?

    問題(question)

    question有以下屬性:

  • type: (String) 提示的類型。默認值:input - 可能的值:input, number, confirm, list, rawlist, expand, checkbox, password, editor。
  • name: (String) 在答案哈希中存儲答案時使用的名稱。如果名字包含句號,它將在答案哈希中定義一個路徑。
  • message: (String|Function) 要打印的問題。如果定義為一個函數,第一個參數將是當前查詢者會話的答案。默認為name的值(后面有一個冒號)。
  • default: (String|Number|Boolean|Array|Function) 如果沒有輸入任何信息,將使用默認值,或者返回默認值的函數。如果定義為一個函數,第一個參數將是當前查詢者會話的答案。
  • choices: (Array|Function) 選擇數組或一個返回選擇數組的函數。如果定義為一個函數,第一個參數將是當前詢問者會議的答案。數組值可以是簡單的numbers, strings, objects(包含一個name(在列表中顯示)、一個value(保存在答案哈希中)和一個short(選擇后顯示)屬性。選擇數組也可以包含一個分隔符。
  • validate: (Function)接收用戶的輸入和答案哈希。如果值是有效的,應該返回true,否則返回一個錯誤信息(String)。如果返回false,會提供一個默認的錯誤信息。
  • filter: (Function) 接收用戶輸入和答案的哈希值。返回過濾后的值,以便在程序內部使用。返回的值將被添加到Answers hash中。
  • transformer: (Function) 接收用戶輸入、答案哈希值和選項標志,并返回一個轉換后的值,顯示給用戶。轉換只影響編輯時顯示的內容。它不會修改答案哈希值。
  • when: (Function, Boolean) 接收當前用戶的答案哈希值,并應根據是否應該問這個問題返回true或false。該值也可以是一個簡單的布爾值。
  • pageSize: (Number)改變使用list、rawList、expand或checkbox時的行數。
  • prefix: (String) 改變默認的前綴信息。
  • suffix: (String)改變默認的后綴信息。
  • askAnswered: (Boolean) 如果答案已經存在,強制提示問題。
  • loop: (Boolean) 啟用列表循環。默認值:true
  • 回答(answers)

    answers:包含用戶在每個提示中的答案的key/value。
    ?

    key:question對象的name屬性
    ?

    value可能的值:
    confirm:Boolean
    input :String 用戶輸入(如果定義了過濾器,則進行過濾)
    number:Number 用戶輸入(如果定義了過濾器,則進行過濾)
    rawlist, list:Number選定的選擇值(如果沒有指定值則為名稱)

    分隔符(Separator)

    分隔符可以被添加到任何choices數組中。
    eg:demo中的 q_5 ,默認是“-”,可以傳入指定字符作為分隔符。
    ?

    完整demo

    #!/usr/bin/env node const inquirer = require("inquirer");const choices = Array.apply(0, new Array(26)).map((x, y) =>String.fromCharCode(y + 65) ); choices.push("Multiline option 1\n super cool feature \n more lines"); choices.push("Multiline option 2\n super cool feature \n more lines"); choices.push("Multiline option 3\n super cool feature \n more lines"); choices.push("Multiline option 4\n super cool feature \n more lines"); choices.push("Multiline option 5\n super cool feature \n more lines"); choices.push(new inquirer.Separator()); choices.push("Multiline option \n super cool feature"); choices.push({name: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium.",value: "foo",short: "The long option", });const requireLetterAndNumber = (value) => {if (/\w/.test(value) && /\d/.test(value)) {return true;}return "Password need to have at least a letter and a number"; };const questions = [{type: "expand",message: "Conflict on `file.js`",name: "q_1",choices: [{key: "y",name: "Overwrite",value: "overwrite",},{key: "a",name: "Overwrite this one and all next",value: "overwrite_all",},{key: "d",name: "Show diff",value: "diff",},new inquirer.Separator("."),{key: "x",name: "Abort",value: "abort",},],},{type: "input",name: "q_2",message: "Question with filtering and validating Text",validate: async () => {await new Promise((r) => setTimeout(r, 3000));return true;},filter: async (answer) => {await new Promise((r) => setTimeout(r, 3000));return `filtered${answer}`;},filteringText: "Filtering your answer...",validatingText: "validating what you wrote",},{type: "input",name: "q_3",message: "Question without filtering and validating Text",validate: async () => {await new Promise((r) => setTimeout(r, 3000));return true;},filter: async (answer) => {await new Promise((r) => setTimeout(r, 3000));return `filtered${answer}`;},},{type: "input",name: "q_4",message: "what‘s your last name",default() {return "Doe";},},{type: "list",name: "q_5",message: "What do you want to do?",choices: ["Order a pizza","Make a reservation",new inquirer.Separator(),"Ask for opening hours",{name: "Contact support",disabled: "Unavailable at this time",},"Talk to the receptionist",],},{type: "list",name: "q_6",message: "What size do you need?",choices: ["Jumbo", "Large", "Standard", "Medium"],filter(val) {return val.toLowerCase();},},{type: "list",name: "q_7",message: "What's your favorite letter?",loop: false,choices,},{type: "checkbox",name: "q_8",message: "Select the letter contained in your name:",choices},{type: "password",name: "q_9",message: "Enter a password",validate: requireLetterAndNumber,},{type: "password",name: "q_10",message: "Enter a masked password",mask: "*",validate: requireLetterAndNumber,},{type: "confirm",name: "q_11",message: "password is ok?",default: true,},{type: "input",name: "q_12",message: "How many do you need?",validate(value) {const valid = !isNaN(parseFloat(value));return valid || "Please enter a number";},filter: Number,},{type: "rawlist",name: "q_13",message: "What do you want to do?",choices: ["Order a pizza","Make a reservation",new inquirer.Separator(),"Ask opening hours","Talk to the receptionist",],},{type: "rawlist",name: "q_14",message: "What size do you need",choices: ["Jumbo", "Large", "Standard", "Medium", "Small", "Micro"],filter(val) {return val.toLowerCase();},},{type: "confirm",name: "q_15",message: "Do you like bacon?",},{type: "input",name: "favorite",message: "Bacon lover, what is your favorite type of bacon?",when(answers) {return answers.q_15;},}, ];inquirer.prompt(questions).then((answers) => {console.log(JSON.stringify(answers, null, " "));inquirer.prompt({ //嵌套type: "list",name: "beverage",message: "And your favorite beverage?",choices: ["Pepsi", "Coke", "7up", "Mountain Dew", "Red Bull"],}); });

    可以注釋問題對象一個個執行對應的問題,分析,不難學會對應的type的用法。
    更多知識請查詢官方文檔,對于大多數場景,上述demo的知識點就夠用了。
    ?

    4.開發cli腳手架

    我們創建vue項目的時候通常使用官方腳手架工具vue-cli 或者使用是自己創建模版項目,里面封裝好了各種配置,通過克隆模版項目進行創建項目。首先對于vue-cli 創建的項目來說,我們每次都需要重新配置各種依賴。比如vue.config.js、axios、vuex、vue-router、ui組件庫等等,重復工作很多。對于使用之前的模版創建的項目,不夠靈活,簡單項目不需要vuex或vue-router,不需要ui組件庫,對于移動端項目,需要vconsole插件、postcss-pxtorem插件。不同的項目需要的插件不同,使用模版項目不夠靈活,只能單獨配置或者抽成多個模版項目。所以我們來搭建一個比較靈活的腳手架easy-vue-cli。在vue-cli的基礎上,再封裝一層,通過命令+詢問的方式安裝插件、配置項目。
    easy-vue-cli選項有是否創建vue.config.js文件、是否使用Gzip壓縮,是否安裝axios,是否安裝Vconsole,是否使用UI組件庫。項目里使用了很多node.js的api,這里不詳細講述,請自行查詢文檔。其實在學會上面的插件的使用后,主要還是要求我們的node.js的功力,發揮自己的想象,實現一些厲害功能。
    主要思路是使用vue create命令創建初始化項目,在初始化項目創建好后,詢問用戶是否安裝插件、生成配置。
    ?

    1.初始化項目

    創建easy-vue-cli項目文件夾。使用命令npm init根據提示創建package.json文件。創建bin文件夾和lib文件夾,分別存放我們的命令文件和主要文件。項目還使用了oraloading 插件。可以先安裝。
    ?

    2.創建主要項目文件

    在bin文件夾新建easy.js文件。文件內容如下:

    #!/usr/bin/env node const { Command } = require("commander"); const program = new Command();program.version(require("../package.json").version);program.command("create <app-name>") //定義腳手架的命令是 create.description("create a vue app and config some dependencies").action((name) => { //獲取項目名,調用創建項目的函數const options = program.opts(); require("../lib/create")(name, options); });program.parse(process.argv); 在`lib`文件夾新建`create.js`文件。文件內容如下: #!/usr/bin/env nodeconst spawn = require("child_process").spawn //spawn 使用給定的 command 和 args 中的命令行參數衍生新進程 const fs = require("fs") const inquirer = require("inquirer"); const generate = require("./generate")/*** @description: delete project*/ function removeDir(filePath) {const stat = fs.statSync(filePath);if(stat.isFile()){fs.unlinkSync(filePath);}else{const files = fs.readdirSync(filePath);console.log(files)if (files.length === 0) {fs.rmdirSync(filePath);} else {let tempFiles = 0;files.forEach((file) => {tempFiles++;const nextFilePath = `${filePath}/${file}`;removeDir(nextFilePath);});//刪除母文件夾下的所有子空文件夾后,將母文件夾也刪除if (tempFiles === files.length) {fs.rmdirSync(filePath);}}} }const choices = [{name: "vue.config.js",checked:true},{name: "Axios",},{name: "Gzip",},{name: "Vconsole",} ];const questions = [{type: "checkbox",name: "configs",message: "Select the config in your app",choices,},{type: "confirm",name: "UI_Components",message: "Do you want to install the ui component library?",},{type: "list",name: "ui",message: "Select the config in your app",choices: ["Element UI", "Ant Design", "Vant"],when(answers) {return answers.UI_Components;},}, ];async function create(projectName,options) {const cmd = spawn("vue", ["create", projectName],{ stdio:["inherit", "inherit", "pipe"]}) //使用vue create 命令創建初始化項目cmd.on("close", function(code, signal){if(code===0){//vue初始化項目創建成功,調用inquirer方法,詢問用戶console.log("vue初始化項目創建成功");inquirer.prompt(questions).then(async (answers) => {if(answers.configs.includes("vue.config.js")){await generate.generateVueConfigJS(projectName);//調用生成vue.config.js的方法}if (answers.configs.includes("Gzip")) {//調用 安裝并配置compression-webpack-plugin 的方法await generate.installCompressionWebpackPlugin(projectName);}});}})process.on("SIGINT", function () {//監聽進程主動關閉,刪掉未創建完的項目console.log("Got SIGINT. Press Control-D/Control-C to exit.");removeDir(projectName);});}module.exports = (...args) => {return create(...args).catch((err) => {error(err);process.exit(1);}); };

    在lib文件夾下新建generate.js文件。文件內容如下:

    const fs = require("fs"); const ora = require("ora"); const spawn = require("child_process").spawn;/*** @description: 生成vue.config.js文件*/ function generateVueConfigJS(projectName) {return new Promise((resolve, reject) => {const spinner = ora({text: "Generating vue.config.js",color: "yellow",});let file = fs.createReadStream(`${__dirname}/vueConfig.js`, {encoding: "utf8",});let out = fs.createWriteStream(`${process.cwd()}/${projectName}/vue.config.js`,{encoding: "utf8",});file.on("data", function (dataChunk) {out.write(dataChunk, function () {spinner.start();});});out.on("open", function (fd) {});file.on("end", function () {out.end("", function () {setTimeout(() => {spinner.succeed("Successfully generated vue.config.js");}, 500);resolve(true);});});}); }//安裝并配置compression-webpack-plugin function installCompressionWebpackPlugin(projectName) {return new Promise((resolve, reject) => {const spinner = ora({text: "install compression-webpack-plugin",color: "yellow",}).start();const cmd = spawn("npm",["install", "compression-webpack-plugin@4.0.0"],{stdio: "pipe",cwd: `${process.cwd()}/${projectName}`,});cmd.on("close", function (code, signal) {if (code === 0) {const content = `const path = require("path")const CompressionWebpackPlugin = require("compression-webpack-plugin")const isProd = process.env.NODE_ENV === "production"function resolve(dir) {return path.join(__dirname, dir)}`;const content2 = `configureWebpack: (config) => {if (isProd) {// 生產環境config.plugins.push(new CompressionWebpackPlugin({// 正在匹配需要壓縮的文件后綴test: /\.(js|css|svg|woff|ttf|json|html)$/,// 大于10kb的會壓縮threshold: 10240,deleteOriginalAssets: false// 其余配置查看compression-webpack-plugin}))}},`;//往固定的行寫入數據const data = fs.readFileSync(`${process.cwd()}/${projectName}/vue.config.js`, "utf8").split("\n");data.splice(0, 0, content);fs.writeFileSync(`${process.cwd()}/${projectName}/vue.config.js`,data.join("\n"),"utf8");const data2 = fs.readFileSync(`${process.cwd()}/${projectName}/vue.config.js`, "utf8").split("\n");data2.splice(data2.length - 45, 0, content2);fs.writeFileSync(`${process.cwd()}/${projectName}/vue.config.js`,data2.join("\n"),"utf8");spinner.succeed("install compression-webpack-plugin success");resolve();} else {spinner.warn("install compression-webpack-plugin error");reject(`install compression-webpack-plugin error`);}});});}module.exports = {generateVueConfigJS,installCompressionWebpackPlugin, }; 分別是生成`vue.config.js`文件和安裝并配置`compression-webpack-plugin`插件的方法。<br />?

    新建vueConfig.js文件,存放我們的vue.config.js文件的模版文件,大家可以靈活調整。

    module.exports = {publicPath: "./", //配置根路徑outputDir: "dist", //構建輸出目錄assetsDir: "assets", //靜態資源目錄(js\css\img)lintOnSave: true, //是否開啟eslintproductionSourceMap: false, // 生產環境是否生成 sourceMap 文件devServer: {},css: {// 是否使用css分離插件 ExtractTextPlugin//如果需要css熱更新就設置為false,打包時候要改為trueextract: false,// 開啟 CSS source maps?sourceMap: process.env.NODE_ENV !== "production",// css預設器配置項// loaderOptions: {// sass: {// prependData: `@import "@/styles/variables.scss";`,// },// },},chainWebpack: (config) => {config.resolve.symlinks(true);(config.entry.app = ["babel-polyfill", "./src/main.js"]),// 別名配置config.resolve.alias.set("@", resolve("src")).set("@utils", resolve("src/utils")).set("@api", resolve("src/api")).set("@components", resolve("src/components")).set("@pic", resolve("src/assets/imgs"));config.resolve.extensions.clear().merge([".js", ".vue", ".json"]);config.optimization.splitChunks({chunks: "all", // 控制webpack選擇哪些代碼塊用于分割(其他類型代碼塊按默認方式打包)。有3個可選的值:initial、async和all。minSize: 30000, // 形成一個新代碼塊最小的體積maxSize: 0,minChunks: 2, // 在分割之前,這個代碼塊最小應該被引用的次數(默認配置的策略是不需要多次引用也可以被分割)maxAsyncRequests: 5, // 按需加載的代碼塊,最大數量應該小于或者等于5maxInitialRequests: 3, // 初始加載的代碼塊,最大數量應該小于或等于3automaticNameDelimiter: "~",name: true,cacheGroups: {libs: {name: "chunk-libs",test: /[\\/]node_modules[\\/]/,priority: 10,chunks: "initial", // only package third parties that are initially dependent},commons: {name: "chunk-commons",test: resolve("src/components"), // can customize your rulesminChunks: 3, // minimum common numberpriority: 5,reuseExistingChunk: true,},},});config.plugins.delete("prefetch-index");config.plugins.delete("preload-index");}, };

    3.配置命令

    在package.json文件中添加bin屬性

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

    4.測試

    ,使用npm安裝我們的腳手架。npm i ../腳手架路徑/?
    使用easy create test命令創建項目,會先調用vue-cli創建初始化項目
    ![截屏2021-07-11 下午2.17.25.png](https://img-blog.csdnimg.cn/img_convert/d1dc9238e65b763961ad77893a6404f6.png#clientId=u9aa3316f-7c82-4&from=ui&id=ueac50833&margin=[object Object]&name=截屏2021-07-11 下午2.17.25.png&originHeight=115&originWidth=391&originalType=binary&ratio=1&size=20558&status=done&style=none&taskId=u5514de1e-e439-4feb-9a8d-173aa1744fe)![截屏2021-07-11 下午2.18.14.png](https://img-blog.csdnimg.cn/img_convert/20eff4da68c52089b3e571aba31ead3b.png#clientId=u9aa3316f-7c82-4&from=ui&id=ub39c2020&margin=[object Object]&name=截屏2021-07-11 下午2.18.14.png&originHeight=400&originWidth=794&originalType=binary&ratio=1&size=62011&status=done&style=none&taskId=ub57a16ba-4a33-4860-8b8e-16b8d55eae8)![截屏2021-07-11 下午2.19.23.png](https://img-blog.csdnimg.cn/img_convert/4c1d7f88b473a95f34f35da29c73f0e2.png#clientId=u9aa3316f-7c82-4&from=drop&id=ua273498e&margin=[object Object]&name=截屏2021-07-11 下午2.19.23.png&originHeight=87&originWidth=474&originalType=binary&ratio=1&size=21168&status=done&style=none&taskId=ud0abdb5a-7a35-4cb0-ae3c-ece9a7474c2)
    項目創建成功,查看test項目,會發現項目生成了vue.config.js文件,安裝并配置好了compression-webpack-plugin
    ?

    5.結束

    這里easy-vue-cli只實現了生成vue.config.js文件和安裝并配置compression-webpack-plugin插件,安裝axios和安裝ui組件并沒有實現,可以參考上述方法自行實現。篇幅太長了。大家可以優化代碼、使用遠程的模版文件等等。這里只是給出一個思路。有改進的地方、想法歡迎提出討論、修改。謝謝。

    最后

    篇幅太長了,謝謝大家能夠耐心看完,我還是小菜雞一枚,歡迎提出改進、錯誤的地方,如果文章內容對你有一點作用,希望可以給一個👍, 沖沖沖!!!。

    總結

    以上是生活随笔為你收集整理的从零开发cli脚手架的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。