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