06.ElementUI 2.X 原始碼學習:原始碼剖析之工程化(一)

Anduril發表於2021-06-03

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 ?目錄結構

image.png

0x.02 ?package.json

接下來將從package.json檔案看起,快速瞭解分析專案 。

package.json 是專案的清單, 定義了這個專案所需要的各種模組,以及專案的配置資訊(比如名稱、版本、許可證、git倉庫等後設資料)。

之前在 05.專案釋出配置(github pages&npm package) 文章中提到了package.json配置資訊,下面將對element 專案的各項屬性進行分析。

專案的 package.json 中有非常多的屬性,可以大致分為以下幾類:

  • 必備屬性(nameversion)
  • 描述資訊(descriptionkeywordshomepagerepositorybugs)
  • NPM指令碼(scripts)
  • 依賴(dependenciesdevDependenciespeerDependencies)
  • 協議(license) 指定軟體的開源協議型別
  • 目錄檔案相關(mainfilestypingsfaasunpkgstyle)

必備屬性

nameversion 屬性是必須的欄位,這兩個屬性組成一個 npm 模組的唯一標識。
name 是一個包的唯一標識,包名不能重複,可以執行 npm view packageName 檢視包名是否已被佔用,並可以檢視一些基本資訊

image.png

依賴

根據此配置資訊,執行 npm || yarn install 命令,自動下載所需的模組,也就是配置專案所需的執行和開發環境。

NPM指令碼

指定了執行指令碼命令的npm命令列縮寫,覆蓋整個專案的生命週期。下文將重點著墨講解。

描述資訊

記錄專案的簡介、關鍵字、專案主頁、程式碼倉庫、反饋issues等元資訊。

協議

開源協議很多有很多,如何為自己的專案選合適的開源協議呢? 可以到 https://choosealicense.com/ 獲取更詳細的說明和指引。

不想勞神費力的可以使用速查圖,參考自 如何選擇開源許可證?
image.png

目錄檔案相關

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.jscommonjs 規範,由build/webpack.common.js打包生成。unpkg屬性值 lib/index.jsumd 規範,由build/webpack.conf.js打包生成。關於打包模組功能會稍後詳細說明。

gaitubao_carbon (66) (1).png

設定 unpkg 屬性後,訪問 https://unpkg.com/element-ui,按照上述規則自動訪問 https://unpkg.com/element-ui@2.15.1/lib/index.js

在之前的me-vue-ui包釋出中,由於沒有配置 unpkg 屬性,訪問 https://unpkg.com/me-vue-ui 時按照 main 屬性定義的檔案路徑自動訪問 https://unpkg.com/me-vue-ui@0.1.2/lib/me-vue-ui.common.js

CDN引入

若瀏覽器環境引入元件,只需要通過 unpkg.com/element-ui 獲取到資源,在頁面上引入 js 和 css 檔案即可開始使用。
CSS檔案的路徑是 style 屬性值;js檔案的路徑是基於umd規範的打包檔案的路徑--unpkg屬性值。

<!-- 引入樣式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入元件庫 -->
<script src="https://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)

下載 (2).png

? 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

image.png

自動下載專案所需的模組,也就是配置專案所需的執行和開發環境。官方推薦使用 yarn

npm run clean

image.png

清除打包/測試生成的目錄及檔案,主要有lib目錄、test\unit\coverage 目錄(跟測試程式碼覆蓋率有關,詳見後文)以及package\theme-chalk\lib目錄(跟主題有關,詳見後文)。

需要安裝 rimraf 包,用於遞迴刪除目錄所有檔案。

npm run eslint 程式碼質量檢查

image.png

基於 .eslintrc.eslintignore 檔案配置,呼叫 eslint 檢測程式碼規範。--quiet引數允報告錯誤,禁止報告警告。

專案使用自己封裝的規則配置 eslint-config-elemefe , 配置使用請參考前文 程式碼風格檢查和格式化配置(ESlint & Prettier)

{
  "extends": "elemefe",
}

檔案構建

npm run i18n

image.png

執行 build/bin/i18n.js 基於 examples/i18n/page.json頁面多語言配置和 examples/pages/template 目錄下的所有模版檔案,生成 zh-CNen-USesfr-FR等四種語言的網站.vue檔案。

npm run build:file

image.png

該命令主要用於檔案的自動化生成,其多個任務是 並行執行

  • 執行 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

image.png

該命令主要用於專案的主題和樣式生成。

  1. 執行 build/bin/gen-cssfile 生成 packages/theme-chalk/index.scss 樣式總入口檔案。全量引入元件時,引用改樣式如下 import 'packages/theme-chalk/src/index.scss'
  2. 採用 gulp 進行樣式構建,將packages/theme-chalk/src下的 scss 檔案轉換成 css 檔案,輸出至packages/theme-chalk/src/lib目錄下;將packages/theme-chalk/src/fonts下的字型檔案壓縮處理,輸出至 packages/theme-chalk/src/lib/fonts 目錄下。
  3. 將構建內容 packages/theme-chalk/lib 拷貝到 lib/theme-chalk 下。前面 sytle 屬性配置的路徑檔案 lib/theme-chalk/index.css 就是這樣生成的。

需要安裝 cp-cli 包,用於檔案和資料夾複製,無需擔心跨平臺問題。

npm run build:utils

image.png

該命令作用把 src 目錄下除了 src/index.js 入口檔案外的其他檔案通過 babel 轉譯後,輸出至 lib 資料夾下。

需要安裝 cross-env 包,是一款執行跨平臺設定和使用環境變數的指令碼,不同平臺使用唯一指令,無需擔心跨平臺問題。

npm run build:umd

image.png

該命令作用是執行 build/bin/build-locale.js 通過 babel 處理 src/locale/lang 目錄下的檔案,生成 umd 格式的檔案,輸出至 lib/umd/locale 目錄下。

專案開發

npm run dev

image.png

該命令用於執行元件庫的本地開發環境。

  1. 執行命令npm run bootstrap 配置專案所需的執行和開發環境。
  2. 執行命令npm run build:file 詳解見前文,構建專案官網相關檔案。
  3. webpack-dev-server 提供一個本地服務(serve) 並執行專案網站(打包規則配置 build/webpack.demo.js);同時執行build/bin/template.js檔案啟動監聽examples/pages/template目 錄下模板檔案,若內容發生變化,則重新生成網站檔案。 webpack-dev-server具有 live reloading功能,網站內容會實時重新載入。

npm run dev:play

image.png

該命令用於元件庫開發中的功能展示,執行效果如下圖。

  1. 執行命令npm run build:file 詳解見前文,構建專案官網相關檔案。
  2. 由於配置瞭如下環境變數NODE_ENV=development PLAY_ENV=true,可以在build/webpack.demo.js打包檔案中看到入口檔案examples/play.js, play.js 引用 examples/play/index.vue, 可以引入元件庫任意元件用於功能展示。

image.png

釋出部署

npm run deploy:build

image.png

該命令作用主要是打包構建專案官網內容,為網站部署做準備。

  1. 執行命令npm run build:file 詳解見前文,構建專案官網相關檔案。
  2. webpack --config build/webpack.demo.js 基於 production 模式,打包生成內容輸出至examples/element-ui/目錄下。
  3. echo element.eleme.io>>examples/element-ui/CNAMEexamples/element-ui/CNAME檔案中寫入element.eleme.ioGithub Docs / Managing a custom domain for your GitHub Pages site

npm run deploy:extension

image.png

該命令作用主要是打包構建主題編輯器的 chorme 外掛專案--Element Theme Roller 官方商城地址 。基於 production 模式打包生成內容輸出至 examples/extension 目錄下。

使用該外掛可以自定義全域性變數和元件的所有設計標記,並實時預覽新主題並基於新主題生成完整的樣式包,以供直接下載

image.png

image.png

? npm run dist ⭐

image.png

該命令作用主要是構建元件庫。

  1. 執行命令npm run clean,詳見上文;
  2. 執行命令npm run build:file,詳見上文;
  3. 執行命令npm run lint,詳見上文;
  4. 執行打包webpack --config build/webpack.conf.js,入口檔案 src/index.jsumd 格式輸出到 lib/index.js;
  5. 執行打包webpack --config build/webpack.common.js,入口檔案 src/index.jscommonjs2格式輸出到 lib/element-ui.common.js;
  6. 執行打包webpack --config build/webpack.component.js,入口檔案 components.json,將packages目錄下的元件,以commonjs2格式分別輸出到lib目錄,用於按需引入;
  7. 執行命令npm run build:utils ,詳見上文;
  8. 執行命令npm run build:umd ,詳見上文;
  9. 執行命令npm run build:theme,詳見上文。

? npm run pub ⭐

image.png

該命令作用主要是元件庫的釋出、程式碼管理。

  1. 執行命令npm run bootstrap,詳見上文;
  2. 執行shell指令碼sh build/git-release.sh ,檢查程式碼 dev 分支是否存在衝突(No conflicts);
  3. 執行shell指令碼sh build/release.sh,合併dev分支到master分支、更新版本號、釋出主題、push程式碼到遠端倉庫、釋出元件庫至NPM;
  4. 執行檔案node build/bin/gen-indices.js,提供 algoliasearch 搜尋功能,需要把 examples/docs 目錄下 .md 檔案內容格式化後上傳 algolia,效果詳見下圖 ?。

image.png

?‍♂️ 在 2.15.x 版本,pub 命令移除了最後一條任務指令sh build/deploy-faas.sh ,用於站點 https://element.eleme.iofaas 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

image.png

該命令用於啟動專案測試,設定了引數 --single-run 執行一次測試之後,karma 會自動停掉。

image.png

npm run test:watch

image.png

該命令用於啟動專案測試,執行結束後會繼續監測檔案是否變更,若發生變更,會重新執行一次測試。
image.png


?? 受制於篇幅的問題,本文到此就結束了!後續文章將會繼續分析學習工程化實踐。

0x.03 ? 連結彙總

點選以下連結,可以快速檢視本系列其他文章:

ElementUI原始碼學習:從零開始搭建Vue元件庫彙總

專欄/Element 2.X 原始碼學習

0x.04 ? 參考

https://zhuanlan.zhihu.com/p/359734011
http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html

相關文章