前端工程化實戰(zhàn)-開發(fā)企業(yè)級CLI
腳手架大家一定都不陌生,比如我們經(jīng)常使用的 vue-cli、create-react-app,它可以幫助我們快速的初始化一個項目,無需從零配置,極大的方便我們的開發(fā)。到這里你可能會疑惑,既然市面上有成熟的腳手架,為什么需要寫一個屬于自己的腳手架呢。因為公共腳手架雖然強大,但并不能滿足我們的實際開發(fā)需求。
例如項目中已有的沉淀,項目架構(gòu)、接口請求的統(tǒng)一處理、換膚、業(yè)務(wù)組件、eslint配置等,這些想要用到新項目中,只能通過復(fù)制粘貼,會存在以下弊端:
重復(fù)性勞動,繁瑣且浪費時間
已有項目沉淀分散在各處,很容易有所遺漏
項目間的配置差異很可能會被忽略
人工操作永遠(yuǎn)都有可能犯錯,建新項目時,總要花時間去排錯
如果我們自己開發(fā)一套腳手架,定制自己的模板,復(fù)制粘貼的人工流程就會轉(zhuǎn)換為 cli 的自動化流程, 還可以通過維護(hù)不同的模板以適應(yīng)不同業(yè)務(wù)需求。既然要開發(fā)一套腳手架,站在巨人肩膀上顯然省事多了,我們先來看看業(yè)界知名腳手架Vue CLI是如何實現(xiàn)的。
2.Vue CLI 原理分析
Vue CLI 是一個基于 Vue.js 進(jìn)行快速開發(fā)的完整系統(tǒng),提供:
通過 @vue/cli 實現(xiàn)的交互式的項目腳手架。
通過 @vue/cli + @vue/cli-service-global 實現(xiàn)的零配置原型開發(fā)。
一個運行時依賴 (@vue/cli-service),該依賴:
可升級;
基于 webpack 構(gòu)建,并帶有合理的默認(rèn)配置;
可以通過項目內(nèi)的配置文件進(jìn)行配置;
可以通過插件進(jìn)行擴展。
一個豐富的官方插件集合,集成了前端生態(tài)中最好的工具。
一套完全圖形化的創(chuàng)建和管理 Vue.js 項目的用戶界面。
2.1全局 vue 執(zhí)行命令存放在哪里
以mac為例,使用命令 where vue,就可以查到 vue 命令所在位置,找到所在位置后,查看目錄即可分析源碼。
2.2vue命令是從哪里注冊的
找到源碼目錄中的package.json,我們會看到如下代碼:
可以看到bin字段指定了可執(zhí)行文件的命令名以及可執(zhí)行文件的路徑,npm安裝一個依賴時,如果該依賴的package.json中指定了bin的信息,那么同時會創(chuàng)建一個全局的軟連接指向該命令所對應(yīng)的可執(zhí)行文件。詳細(xì)可查看npm的官方文檔:package.json中bin的使用說明。
2.3依賴包分析
包名 | 用途 |
commander | 完整的 node.js 命令行解決方案, Commander 負(fù)責(zé)將參數(shù)解析為選項和命令參數(shù) |
shelljs | 用來執(zhí)行shell命令 |
inquirer | 通用交互式命令行用戶界面的集合 |
semver | 語義化版本控制 |
chalk | 設(shè)置終端字符串樣式 |
2.4腳手架都做了哪些事情
2.4.1 HTML 和靜態(tài)資源
html文件是一個會被 html-webpack-plugin 處理的模板。在構(gòu)建過程中,資源鏈接會被自動注入。另外,Vue CLI 也會自動注入 resource hint (preload/prefetch、manifest 和圖標(biāo)鏈接 (當(dāng)用到 PWA 插件時) 以及構(gòu)建過程中處理的 JavaScript 和 CSS 文件的資源鏈接。
2.4.2 CSS 相關(guān)
Vue CLI生成項目支持 PostCSS、CSS Modules 和包含 Sass、Less、Stylus 在內(nèi)的預(yù)處理器,你可以在創(chuàng)建項目的時候選擇預(yù)處理器。
2.4.3 webpack 相關(guān)
Vue CLI基于 webpack 構(gòu)建,并帶有合理的默認(rèn)配置,可以通過項目內(nèi)的配置文件進(jìn)行配置,還可以通過插件進(jìn)行擴展。
2.4.4 模式與環(huán)境變量
模式是 Vue CLI 項目中一個重要的概念。默認(rèn)情況下,一個 Vue CLI 項目有三個模式:
development 模式用于 vue-cli-service servetest 模式用于 vue-cli-service test:unitproduction 模式用于 vue-cli-service build 和 vue-cli-service test:e2e你可以通過傳遞 --mode 選項參數(shù)為命令行覆寫默認(rèn)的模式。
2.4.5 構(gòu)建目標(biāo)
當(dāng)你運行 vue-cli-service build 時,你可以通過 --target 選項指定不同的構(gòu)建目標(biāo)。它允許你將相同的源代碼根據(jù)不同的用例生成不同的構(gòu)建。
通過以上對Vue CLI 的分析,我們就對腳手架工具提供的構(gòu)建集成能力有了一個大概的了解。這有助于我們在使用具體工具時快速定位問題的邊界,當(dāng)我們自己設(shè)計腳手架的時候,我們也可以參照和借鑒,可以適用于我們業(yè)務(wù)的有:
- 通過命令行與用戶交互
- 根據(jù)用戶的選擇生成對應(yīng)的文件,實現(xiàn)的零配置原型開發(fā)
需要做出修改的部分有:
- 基于 vite 構(gòu)建,并帶有合理的默認(rèn)配置;
- 預(yù)定義業(yè)務(wù)模板,根據(jù)用戶選擇生成
- 業(yè)務(wù)模板基礎(chǔ)支持:
HTML 和靜態(tài)資源處理
內(nèi)置css預(yù)處理器
內(nèi)置vite配置,可以直接修改vite配置文件
內(nèi)置test、pre、pro三種模式,并生成對應(yīng)的配置文件
按照上述總結(jié),讓我們一步一步編寫自己的腳手架吧,首先是通過命令行與用戶交互,那么我們需要有一個可執(zhí)行命令的名字,也是腳手架的名字,這里我們就叫做dt-fe-cli
3.腳手架實現(xiàn)
3.1命令行工具編寫
3.1.1 初始化項目
我們的腳手架叫做dt-fe-cli,創(chuàng)建dt-fe-cli文件夾,執(zhí)行npm init -y初始化倉庫,生成package.json文件。
在dt-fe-cli文件夾下創(chuàng)建bin文件夾,并在里面創(chuàng)建cli.mjs文件,此文件作為我們腳手架的入口,需要將其配置到package.json的bin字段。
這樣我們腳手架的入口就有了,繼續(xù)編寫腳手架的功能吧
3.1.2 指令
dt-fe-cli 作為全局命令,同時提供了很多指令。
dt-fe-cli --version可以查看 dt-fe-cli 版本
dt-fe-cli --help可以查看幫助文檔
dt-fe-cli create xxx可以創(chuàng)建一個項目 ...
3.1.3 create命令
create接受一個項目名作為參數(shù),這里還提供了額外選項-f, --force,此選項代表如果本地已經(jīng)存在同名文件夾,是否覆寫。命令行解決方案需要依賴第三方庫commander
3.1.4 create方法設(shè)計與實現(xiàn)
執(zhí)行create命令后,如何創(chuàng)建項目呢,我們公司的項目都是托管在內(nèi)部gitlab上面的,所以直接使用git clone去拉取模板項目,這里需要依賴第三方庫shelljs,那么這里就需要首先判斷git是否存在,不存在提示并退出。之前我們還寫了一個額外選項,用來表示如果本地已經(jīng)存在同名文件夾,是否覆寫。若沒有此選項,還需要交互式的詢問,這里需要依賴第三方庫inquirer。
create.mjs:
3.1.5 node版本檢查
執(zhí)行 create 命令后,創(chuàng)建項目會去 gitlab 拉取代碼下載我們自定義的模版,目前我使用的模版均由 Vite3 創(chuàng)建,Vite3 需要 Node.js 版本 14.18+,16+。所以在使用腳手架時,可以先檢查一下當(dāng)前 Node.js 版本是否符合,不符合則拋出異常。當(dāng)前依賴的 Node.js 版本需要將其配置到package.json的engines字段,判斷當(dāng)前 Node.js 版本是否符合需要依賴第三方庫semver
package.json:
cli.mjs:
到這里,腳手架的基本功能就已經(jīng)開發(fā)完畢了,剩下的就是我們的項目模板了
3.2模版設(shè)計支持功能
3.2.1 TypeScript
使用 Vite3 構(gòu)建,Vite3 天然支持引入 .ts 文件
3.2.2 打包自動上傳CDN
3.2.3 commit 校驗
3.2.4 eslint校驗
ESLint通用配置的部分這里就不再贅述了,這里介紹一下我們業(yè)務(wù)里面自定義的ESLint插件。eslint校驗大家都很熟悉,市面上也有很多eslint插件,但隨著項目不斷迭代發(fā)展,我們團(tuán)隊的編碼規(guī)范使用現(xiàn)有的eslint插件已經(jīng)無法滿足了,需要自己創(chuàng)建插件,并融入到cli的模板當(dāng)中。
創(chuàng)建插件
開始創(chuàng)建插件的最簡單方法是使用 Yeoman 生成器。生成器將指導(dǎo)您設(shè)置插件的骨架
以上命令會生成如下目錄
插件可以在 ESLint 中使用的額外規(guī)則。為此,插件必須導(dǎo)出一個包含規(guī)則 ID 到規(guī)則的鍵值映射的規(guī)則對象,舉個簡單的例子,我們想創(chuàng)建一條不允許使用console.log的規(guī)則
創(chuàng)建規(guī)則
此命令會在lib/rules文件夾下創(chuàng)建一個新的js文件,一個規(guī)則對應(yīng)一個可導(dǎo)出的 node 模塊
上面這段代碼是一個規(guī)則的源碼文件的基本格式,一個規(guī)則的源文件輸出一個對象,它由 meta 和 create 兩部分組成。
- meta(對象)包含規(guī)則的元數(shù)據(jù),如規(guī)則類型、文檔、可接受參數(shù)的schema等等
- create (function) 返回一個對象,其中包含了 ESLint 在遍歷 JavaScript 代碼的抽象語法樹 AST (ESTree 定義的 AST) 時,用來訪問節(jié)點的方法。
核心其實在于create方法,我們?nèi)粝胫廊绾尉帉慶reate方法,首先要明白其原理,那就是 ESLint 是如何分析我們所編寫的代碼呢?相信大家對此也都有所了解,沒錯,就是AST (Abstract Syntax Tree(抽象語法樹))
插件原理
ESLint 解析器將代碼轉(zhuǎn)換為 ESLint 可以評估的抽象語法樹。默認(rèn)情況下,ESLint 使用內(nèi)置的 Espree 解析器,它與標(biāo)準(zhǔn)的 JavaScript 運行時和版本兼容,然后去攔截檢測是否符合我們規(guī)定的書寫方式,最后讓其展示報錯、警告或正常通過。ESLint 的核心就是規(guī)則(rules),而定義規(guī)則的核心就是利用 AST 來做校驗,那就讓我們看一下代碼 AST 中會表現(xiàn)為什么樣子。
上圖可以看出,console.log對應(yīng) AST 中type為ExpressionStatement(表達(dá)式語句),表達(dá)式類型為CallExpression(調(diào)用表達(dá)式),被調(diào)用者類型為MemberExpression(成員表達(dá)式),被調(diào)用對象名為console,屬性名為log,根據(jù)上述信息,我們就可以來完善create方法了
編寫規(guī)則
至此,包含一條規(guī)則(禁止使用console.log)的 ESLint 插件就編寫完成了,接下來將此項目發(fā)布到npm平臺就可以在項目模板中下載使用了
最后
本文介紹了如何從零編寫一個我們自己的腳手架,并且可以根據(jù)不同業(yè)務(wù)場景區(qū)分模版,把業(yè)務(wù)已有的積累沉淀進(jìn)去,以上便是本次分享的全部內(nèi)容,希望對你有所幫助 ^_^
作者簡介
馬春鍵
主機廠事業(yè)部,技術(shù)部
2021年加入汽車之家,目前任職于主機廠事業(yè)部-技術(shù)部-數(shù)科技術(shù)及系統(tǒng)團(tuán)隊-前端開發(fā)組,主要負(fù)責(zé)數(shù)科前端業(yè)務(wù),前端前沿技術(shù)探索等工作