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

👉 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,用於站點 https://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 📚 參考
https://zhuanlan.zhihu.com/p/359734011
http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html
