06.ElementUI 2.X 源碼學習:源碼剖析之工程化(一)
- 2021 年 6 月 3 日
- 筆記
- 0x02.FrontEnd, element
0x.00 前言
在用了5章篇幅 ElementUI源碼學習:從零開始搭建Vue組件庫匯總 講解了如何編寫一個組件、發布npm以及生成展示文檔之後。接下來將分析Element項目的程式碼結構,學習其工程化思想。
前端開發早已從瘦客戶端(thin client)架構/中心伺服器(server-centric)架構進入到胖客戶端(fat client)架構,各家技術百花齊放,讓人目不暇接,直呼學不動了!伴隨項目開發的複雜度日益增高,開發中需要直面各種問題: 開發效率、產品品質、多人協作等。
前端工程化是什麼?
前端工程化 就是為了應對上述問題,把軟體工程相關的方法和思想應用到前端日常開發中,以 系統化的、規範化的、可度量的方法 用於前端項目的開發、運行和維護等階段,從而提高開發效率、提高產品品質、減少不必要的重複工作時間、降低開發難度/風險、降低企業成本(降本增效)。
如今前端項目的開發、構建、部署等主要環節,涉及了項目構建、程式碼開發、分支管理、自動化測試、持續集成、項目部署、性能等內容。如何用工程化的思想讓開發更加系統化、標準化?主要分為 模組化(組件化)、規範化、自動化 等多個方面。
1️⃣ 模組化
項目按照其功能/業務拆分成相互獨立的模組,可以獨立運行。每個模組只包含與其功能相關的內容,模組之間通過介面調用,降低模組間的耦合。 將一個大的系統模組化之後,每個模組都可以被高度復用。模組不等於功能,一個功能可能包含多個模組(功能 > 模組)。
模組化讓項目便於依賴管理、利於性能優化、提高可維護性。 各技術實現方案如下:
- JS的模組化(CommonJS、 AMD、 CMD、UMD、ES6 Module)
- CSS的模組化(BEM 命名規範、CSS Module、CSS In JS)
- 資源模組化(webpack loaders)
2️⃣ 組件化
組件化是為了解決項目程式碼重複問題,將其拆分成多個獨立的組件給不同的功能使用,提高系統的程式碼重用(復用)性和易維護性。
📝 模組化vs組件化
組件化和模組化的中心思想都是 分而治之
, 將一個項目拆分成更小的顆粒度的單元(組件/模組
),降低業務開發的複雜度。
模組化、組件化它們術語是相似的,作為分治思想的體現,最終都實現了
高內聚,低耦合
。
通常認為「模組」比「組件」大。 項目模組化不一定要求組件化,在進行模組化拆分時可以完全不考慮程式碼重用。一般不會這麼做,這不是 best practice。
組件化就如UI控制項,可以在各個模組中使用。而模組化就比如一個消息列表介面,引用table組件實現。雖然它沒有復用的需求,但我們也要把它封裝成獨立模組。
3️⃣ 規範化
規範是團隊基本約定的內容,必須嚴格遵循,旨在增強團隊開發協作、提高程式碼品質。
- 目錄結構 (約定俗稱)
- 編碼規範[HTML、CSS、JS、圖片、命名等規範] (eslint、prettier)
- 前後端介面規範 (Swagger RESTful)
- 組件文檔規範
- Git分支管理 (Git Flow )
- Commit描述規範 (Commitizen)
- 設計規範 (Material Design、Ant Design)
4️⃣ 自動化
將工作流程內容標準化,通過工具實現全/半自動化完成重複的工作,減少人的操作,實現標準統一、高品質交付。
- 文件構建 (webpack)
- 持續集成/構建/部署 (Travis CI)
- 自動化測試 (Jasmine、Mocha+chai、Jest)
下面將通過解析element項目源碼,從結構、功能、源碼方面逐一解析,學習其模組化、組件化、規範化、自動化等多維度優秀實踐。
0x.01 📁目錄結構
0x.02 📃package.json
接下來將從package.json
文件看起,快速了解分析項目 。
package.json
是項目的清單, 定義了這個項目所需要的各種模組,以及項目的配置資訊(比如名稱、版本、許可證、git倉庫等元數據)。
之前在 05.項目發布配置(github pages&npm package) 文章中提到了package.json
配置資訊,下面將對element
項目的各項屬性進行分析。
項目的 package.json
中有非常多的屬性,可以大致分為以下幾類:
- 必備屬性(
name
、version
) - 描述資訊(
description
、keywords
、homepage
、repository
、bugs
) - NPM腳本(
scripts
) - 依賴(
dependencies
、devDependencies
、peerDependencies
) - 協議(
license
) 指定軟體的開源協議類型 - 目錄文件相關(
main
、files
、typings
、faas
、unpkg
、style
)
必備屬性
name
和 version
屬性是必須的欄位,這兩個屬性組成一個 npm 模組的唯一標識。
name
是一個包的唯一標識,包名不能重複,可以執行 npm view packageName
查看包名是否已被佔用,並可以查看一些基本資訊
依賴
根據此配置資訊,運行 npm || yarn install
命令,自動下載所需的模組,也就是配置項目所需的運行和開發環境。
NPM腳本
指定了運行腳本命令的npm命令行縮寫,覆蓋整個項目的生命周期。下文將重點著墨講解。
描述資訊
記錄項目的簡介、關鍵字、項目主頁、程式碼倉庫、回饋issues等元資訊。
協議
開源協議很多有很多,如何為自己的項目選合適的開源協議呢? 可以到 //choosealicense.com/ 獲取更詳細的說明和指引。
不想勞神費力的可以使用速查圖,參考自 如何選擇開源許可證?
目錄文件相關
main
main
屬性指定程式的主入口文件,當在應用程式中導入此軟體包時,應用程式會在該位置搜索模組的導出。在程式碼中引入整個 Element import ElementUI from 'element-ui';
,實際上引入的就是 lib/element-ui.common.js
中暴露出去的模組。
{
"main": "lib/element-ui.common.js",
}
files
files
屬性用於描述 npm publish
後推送到 npm 伺服器的文件列表,如果指定文件夾,則文件夾內的所有內容都會包含進來。也可以通過配置 .npmignore
文件來忽略文件上傳。
{
"files": [
"lib",
"src",
"packages",
"types"
],
}
typings
typings
屬性指定針對typescript的聲明文件入口。
{
"main": "lib/element-ui.common.js",
"typings": "types/index.d.ts",
}
詳細參考 TypeScript docs.
style
style
屬性指定了樣式入口文件。
{
"style": "lib/theme-chalk/index.css",
}
unpkg
unpkg
是一個前端常用的公共 CDN,它通過 URL 語法可以訪問NPM上任何包的任何文件。
unpkg.com/:package@:version/:file
當把包發布到 npm 上時, 不僅可以 NodeJs 環境使用,也可以通過 unpkg 獲取在瀏覽器環境執行,不過需要符合 umd
規範。
{
"unpkg": "lib/index.js",
}
main
屬性值lib/element-ui.common.js
是 commonjs
規範,由build/webpack.common.js
打包生成。unpkg
屬性值 lib/index.js
是 umd
規範,由build/webpack.conf.js
打包生成。關於打包模組功能會稍後詳細說明。
設置 unpkg
屬性後,訪問 //unpkg.com/element-ui,按照上述規則自動訪問 //unpkg.com/[email protected]/lib/index.js。
在之前的me-vue-ui
包發布中,由於沒有配置 unpkg
屬性,訪問 //unpkg.com/me-vue-ui 時按照 main
屬性定義的文件路徑自動訪問 //unpkg.com/[email protected]/lib/me-vue-ui.common.js。
CDN引入
若瀏覽器環境引入組件,只需要通過 unpkg.com/element-ui 獲取到資源,在頁面上引入 js 和 css 文件即可開始使用。
CSS文件的路徑是 style
屬性值;js文件的路徑是基於umd
規範的打包文件的路徑–unpkg
屬性值。
<!-- 引入樣式 -->
<link rel="stylesheet" href="//unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入組件庫 -->
<script src="//unpkg.com/element-ui/lib/index.js"></script>
faas
用於faas deploy
配置。NPM腳本的pub
命令存在 sh build/deploy-faas.sh
調用,用於站點element.eleme.io
的發布部署,不過在2.15版本之後被移除了(具體使用情況無法重現)。詳見commit feat: add change log 2.15.0 (#20692)
👉 NPM腳本 ⭐
scripts
屬性指定了運行腳本命令的npm命令行縮寫,各個腳本可以互相組合使用,這些腳本覆蓋整個項目的生命周期(開發、測試、打包、部署)。
腳本使用注意事項
通配符
由於 npm 腳本就是 Shell 腳本,因為可以使用 Shell 通配符。
"lint": "jshint *.js"
"lint": "jshint **/*.js"
上面程式碼中,*
表示任意文件名,**
表示任意一層子目錄。
執行順序
如果 npm 腳本裡面需要執行多個任務,需要明確它們的執行順序。如果是 並行執行(即同時的平行執行),使用&
符號。
$ npm run script1.js & npm run script2.js
如果是 繼發執行(即只有前一個任務成功,才執行下一個任務),使用 &&
符號。
$ npm run script1.js && npm run script2.js
element
項目定義了很多腳本,按照用途大致分為項目基礎、文件構建、項目開發 、發布部署、項目測試等。
腳本命令調用 build 目錄中的眾多文件,自動完成大量重複性工作,從而減少人為錯誤、提高效率。下面將逐一分析講解腳本命令的功能和作用。
關於
/build
目錄下文件功能,本篇幅不做講解,詳見後續文章。
項目基礎
npm run bootstrap
自動下載項目所需的模組,也就是配置項目所需的運行和開發環境。官方推薦使用 yarn
。
npm run clean
清除打包/測試生成的目錄及文件,主要有lib
目錄、test\unit\coverage
目錄(跟測試程式碼覆蓋率有關,詳見後文)以及package\theme-chalk\lib
目錄(跟主題有關,詳見後文)。
需要安裝
rimraf
包,用於遞歸刪除目錄所有文件。
npm run eslint 程式碼品質檢查
基於 .eslintrc
和 .eslintignore
文件配置,調用 eslint
檢測程式碼規範。--quiet
參數允報告錯誤,禁止報告警告。
項目使用自己封裝的規則配置 eslint-config-elemefe
, 配置使用請參考前文 程式碼風格檢查和格式化配置(ESlint & Prettier)。
{
"extends": "elemefe",
}
文件構建
npm run i18n
執行 build/bin/i18n.js
基於 examples/i18n/page.json
頁面多語言配置和 examples/pages/template
目錄下的所有模版文件,生成 zh-CN
、en-US
、es
、fr-FR
等四種語言的網站.vue
文件。
npm run build:file
該命令主要用於文件的自動化生成,其多個任務是 並行執行。
- 執行
build/bin/iconInit.js
生成examples/icon.json
圖標集合文件。 - 執行
build/bin/build-entry.js
生成src/index.js
組件庫入口文件。 - 執行
build/bin/i18n.js
生成官網的多語言網站文件。 - 執行
build/bin/version.js
生成examples/version.json
記錄項目版本資訊,用於網站版頭部導航版本切換。
npm run build:theme
該命令主要用於項目的主題和樣式生成。
- 執行
build/bin/gen-cssfile
生成packages/theme-chalk/index.scss
樣式總入口文件。全量引入組件時,引用改樣式如下import 'packages/theme-chalk/src/index.scss'
。 - 採用
gulp
進行樣式構建,將packages/theme-chalk/src
下的scss
文件轉換成css
文件,輸出至packages/theme-chalk/src/lib
目錄下;將packages/theme-chalk/src/fonts
下的字體文件壓縮處理,輸出至packages/theme-chalk/src/lib/fonts
目錄下。 - 將構建內容
packages/theme-chalk/lib
拷貝到lib/theme-chalk
下。前面sytle
屬性配置的路徑文件lib/theme-chalk/index.css
就是這樣生成的。
需要安裝
cp-cli
包,用於文件和文件夾複製,無需擔心跨平台問題。
npm run build:utils
該命令作用把 src
目錄下除了 src/index.js
入口文件外的其他文件通過 babel
轉譯後,輸出至 lib
文件夾下。
需要安裝
cross-env
包,是一款運行跨平台設置和使用環境變數的腳本,不同平台使用唯一指令,無需擔心跨平台問題。
npm run build:umd
該命令作用是執行 build/bin/build-locale.js
通過 babel
處理 src/locale/lang
目錄下的文件,生成 umd
格式的文件,輸出至 lib/umd/locale
目錄下。
項目開發
npm run dev
該命令用於運行組件庫的本地開發環境。
- 執行命令
npm run bootstrap
配置項目所需的運行和開發環境。 - 執行命令
npm run build:file
詳解見前文,構建項目官網相關文件。 webpack-dev-server
提供一個本地服務(serve) 並運行項目網站(打包規則配置build/webpack.demo.js
);同時執行build/bin/template.js
文件啟動監聽examples/pages/template
目 錄下模板文件,若內容發生變化,則重新生成網站文件。 webpack-dev-server具有 live reloading功能,網站內容會實時重新載入。
npm run dev:play
該命令用於組件庫開發中的功能展示,運行效果如下圖。
- 執行命令
npm run build:file
詳解見前文,構建項目官網相關文件。 - 由於配置了如下環境變數
NODE_ENV=development PLAY_ENV=true
,可以在build/webpack.demo.js
打包文件中看到入口文件examples/play.js
,play.js
引用examples/play/index.vue
, 可以引入組件庫任意組件用於功能展示。
發布部署
npm run deploy:build
該命令作用主要是打包構建項目官網內容,為網站部署做準備。
- 執行命令
npm run build:file
詳解見前文,構建項目官網相關文件。 webpack --config build/webpack.demo.js
基於production
模式,打包生成內容輸出至examples/element-ui/
目錄下。echo element.eleme.io>>examples/element-ui/CNAME
往examples/element-ui/CNAME
文件中寫入element.eleme.io
。Github Docs / Managing a custom domain for your GitHub Pages site
npm run deploy:extension
該命令作用主要是打包構建主題編輯器的 chorme 插件項目–Element Theme Roller
官方商城地址 。基於 production
模式打包生成內容輸出至 examples/extension
目錄下。
使用該插件可以自定義全局變數和組件的所有設計標記,並實時預覽新主題並基於新主題生成完整的樣式包,以供直接下載
👉 npm run dist ⭐
該命令作用主要是構建組件庫。
- 執行命令
npm run clean
,詳見上文; - 執行命令
npm run build:file
,詳見上文; - 執行命令
npm run lint
,詳見上文; - 執行打包
webpack --config build/webpack.conf.js
,入口文件src/index.js
以umd
格式輸出到lib/index.js
; - 執行打包
webpack --config build/webpack.common.js
,入口文件src/index.js
以commonjs2
格式輸出到lib/element-ui.common.js
; - 執行打包
webpack --config build/webpack.component.js
,入口文件components.json
,將packages
目錄下的組件,以commonjs2
格式分別輸出到lib
目錄,用於按需引入; - 執行命令
npm run build:utils
,詳見上文; - 執行命令
npm run build:umd
,詳見上文; - 執行命令
npm run build:theme
,詳見上文。
👉 npm run pub ⭐
該命令作用主要是組件庫的發布、程式碼管理。
- 執行命令
npm run bootstrap
,詳見上文; - 運行shell腳本
sh build/git-release.sh
,檢查程式碼dev
分支是否存在衝突(No conflicts); - 運行shell腳本
sh build/release.sh
,合併dev分支到master分支、更新版本號、發布主題、push程式碼到遠程倉庫、發布組件庫至NPM; - 執行文件
node build/bin/gen-indices.js
,提供algoliasearch
搜索功能,需要把examples/docs
目錄下.md
文件內容格式化後上傳algolia
,效果詳見下圖 👇。
💁♂️ 在
2.15.x
版本,pub
命令移除了最後一條任務指令sh build/deploy-faas.sh
,用於站點 //element.eleme.io 的faas deploy
。
測試
實現項目自動化測試。
- karma測試執行過程管理工具(Test Runner)。
- Mocha 是運行在 Node.js 和瀏覽器上的功能豐富的 JavaScript 測試框架。
- Chai 是一個用於 Node.js 和瀏覽器的 BDD/TDD 斷言庫,可以與任何 JavaScript 測試框架便捷配對。
- Sinon.JS 用於對 JavaScript 隔離測試 spy, stub 和 mock。適用於任何單元測試框架。
測試腳本命名方式為 [組件名].spec.js
, 統一放在 test/unit/specs/
目錄下。如果測試成功,karma-coverage
會在 test/unit/coverage
文件夾中生成測試覆蓋率結果的網頁。
npm run test
該命令用於啟動項目測試,設置了參數 --single-run
執行一次測試之後,karma 會自動停掉。
npm run test:watch
該命令用於啟動項目測試,執行結束後會繼續監測文件是否變更,若發生變更,會重新執行一次測試。
👋👋 受制於篇幅的問題,本文到此就結束了!後續文章將會繼續分析學習工程化實踐。
0x.03 🔖 鏈接匯總
點擊以下鏈接,可以快速查看本系列其他文章:
0x.04 📚 參考
//zhuanlan.zhihu.com/p/359734011
//www.ruanyifeng.com/blog/2016/10/npm_scripts.html