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

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等元資訊。

協議

開源協議很多有很多,如何為自己的項目選合適的開源協議呢? 可以到 //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 屬性後,訪問 //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)

下載 (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 ,用於站點 //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 📚 參考

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