前端工程化指的是什么?
大家好,我是前端西瓜哥。今天來看看前端工程化是什么。
什么是前端工程化?
工程化,可以理解為使用一些方式,去改良然后提高行業中現有的步驟、設計、應用方式。前端工程化,就是指對前端進行一些流程的標準化,讓開發變得更有效率,且更好地做產品交付。
一開始,網頁頁面并不復雜,只是提供一些簡單的展示和交互的靜態頁面,甚至不需要后端。
后來需要根據不同用戶返回不同的頁面信息,此時我們會用后端讀取數據,配合一些模板引擎,在后端拼接好內容再返回,這就是所謂的服務端渲染(SSR)。
再后來,頁面變得非常復雜,于是出現了前后端分離,前端被單獨拎了出來,專門寫 html、css 和 js,變成了 單頁面應用(SPA)。但復雜也帶來了很多問題,比如多個腳本的執行時機不對、css 名沖突、文件過于臃腫、錯誤的緩存導致沒能下載最新的資源,等前端復雜后出現的一系列問題。
隨著 Nodejs 的誕生,我們可以用 JS 去寫前端工具了。為了解決上面這些問題,前端界出現了一大堆的工具和框架:Gulp、Angular、babel、Sass、React、Vue、Webpack、Yarn、TypeScript、ESLint、Docker、k8s 等等。
一切都是為了讓前端的開發更工程化,也就是 不停地改良前端項目的開發流程,讓開發者能夠更高效地開發、更好地進行團隊協作、讓代碼的風格標準化、對資源做壓縮以及懶加載、更好地交付部署等。
(當然也因為工具過多,讓前端直呼 “學不動了”)
我們通過四個維度來談談前端工程化一些具體的細節,分別是:
- 模塊化
- 組件化
- 規范化
- 自動化
模塊化
模塊化,指的是將代碼功能做拆分,分成獨立地單能相互依賴的片段。
首先是 JS 的模塊化。
JS 一開始的職責是給網頁提供一些簡單的交互,所以語法相對簡單且不支持模塊化。隨著網頁的復雜,發現原來的組織方式帶來了很多問題,變得難以維護。
于是 CommonJS、AMD、ES Module 等模塊系統出現了。正統標準是 ES Module,通過 import 關鍵字引入模塊,通過 export 導出模塊。
JS 的模塊化將代碼做了拆分,解決了全局變量污染、依賴關系不清晰、多人協作不方便、腳本引入順序、單元測試等問題。
CSS 的模塊化。
?CSS 的第一個問題是比較難寫,比如不支持選擇器嵌套,對此我們可以用 CSS 預編譯器(比如 Less、Sass、Stylus)去寫一些更高級的語法,然后編譯成 CSS。
然后是就 命名沖突問題,一種舊的方案是 BEM,就是通過將 CSS 命名 在組件化的框架中,我們有很多方案,可以用 CSS in JS,也可以用 CSS Module,或者 Vue 特有的 CSS Scoped。
HTML 的模塊化。
html 通常是動態的,在服務端我們會使用模板引擎(template),將得到的數據注入到占位符中。在后端 Nodejs,我們可以用 pug、handlebars、ejs 等。
前后端分離后,我們通常使用的是 Vue 的 template(類似 handlebars 語法)以及 React 的 JSX。
資源整合模塊化
不同類型的資源無法組織在一起,比如 JS 引擎能識別引入的 js 文件,但無法識別 css 文件。如果我們希望所有的資源都能組織再一起進行管理,要分別管理一個個不同類型的資源要方便地多。
為了解決這個問題,webpack 誕生了。webpack 是一個模塊打包器,能夠將任何資源轉換為 js 代碼進行導入。比如圖片,它可以先變成一個靜態資源服務的一個資源,然后在 js 文件 import 的時候在轉換為一個 url 字符串,或者直接就變成一個 base64 字符串。
這些需要使用到一些 loader(加載器)。webpack 是一個框架,使用者需要根據需求,添加一些 loader,去識別不同的文件,轉化成 JS 代碼導入。
此外還有 plugin(插件),在這整個流程中做一些處理,比如將導出的 JS 文件插入到 HTML 模板中,或是進行代碼的壓縮等等。
組件化
組件化是 UI 層面上的更細粒度的拆分,一種類似 div 等原生元素的 “自定義元素”。
組件有自己的 HTML、CSS 和 JS,同時有自己的狀態,并支持嵌入到其他組件中并接受外部的數據,可以進行復用。組件化可以看作是 UI 層組織方式的一種模塊化。
目前主流的 React 和 Vue 前端框架都是基于組件的。
原本的以資源類型為單位進行組織的管理(所有 JS 文件放一個文件夾、CSS 同理),其實維護起來比較困難,也不好復用,組件化的構想是以視覺為單位進行拆分,做了結構、樣式、腳本的組裝,抽象出一個 “新的元素”。
組件已經是前端開發的基石了,是一種比較合理的抽象。
規范化
然后就是前端代碼的規范。規范是很重要的,能讓代碼能夠寫得更容易更正確,避免一些不必要的錯誤。
能想到的規范有:
- 目錄結構規定。
- 代碼風格(包括 JS、HTML、CSS)。
- 注釋規范。
- commit message 規范。
- git 工作流規范。
- Code Review。
- 請求接口規范。
有些規范不太能用工具進行限制,比如目錄結構。但有些規范是可以利用到工具的,下面來說說有哪些和規范相關的工具。
首先是重磅級的 TypeScript。
TS 是有類型的 JS,是 JS 的超集。通過類型,我們可以預測變量的行為,比如一個布爾值類型是不能被作為函數調用的,可能為 undefined 的值需要進行類型收窄后丟棄 undefined 的可能性才能使用。
TS 越來越流行,是因為在大型項目中,類型系統是非常重要的,能夠避免大量的類型錯誤。TS 讓代碼即文檔,降低程序員理解代碼的成本。
如果工具層就能做規范,就不應該用文檔去說明,人不是絕對正確的機器,但工具可以。
然后是 ESLint。
ESLint 能夠檢測 JS 代碼中的錯誤,主要兩個方面:
- 代碼質量,比如你不能聲明一個沒有被使用的變量。
- 代碼風格,比如字符串引號必須用單引號。
ESLint 有助于統一團隊的風格,讓代碼看起來基本像是一個人寫的,避免出現字符串一會用單引號,一會用雙引號,變量命名一會用下劃線風格,一會用駝峰風格,這種讓強迫癥抓狂的情況。
然后是 commit message。commit message 我們不希望看到像是 “修復了一些 bug” 這種不夠具體的寫法,希望具有一定的結構,比如 "fix(工作臺): 修復了卡片不能滾動的問題"。
對此我們可以用 commitlint 的命令行工具去判斷是否符合特定風格。
當然還需要確保團隊成員是使用了這些工具的,我們可以用保存后自動格式化(需要配合編輯器和對應插件)。然后最重要的就是 git hook,可以在本地 commit 時先對 staged 中的文件做風格校驗和格式化,然后再檢查 commit messge 風格是。如果不對,本地 commit 會失敗。對應的工具是 husky。
自動化
重復的可以自動化的流程化工作,應該盡量去自動化。讓人去做,對人是一種折磨,然后也不能保證質量,因為通常流程也很復雜,即使是簡單,做多了也容易錯。
一個小概率事件只要做的次數足夠多,它就會變成大概率事件。這也是為什么分布式系統中容錯機制是非常重要的原因。
首先想到的自然是 CI/CD(持續集成和持續交付/部署)。我們將代碼提交到遠端倉庫時,或者是給一個分支打了 tag 后,能夠觸發一些腳本,將我們的項目代碼做打包編譯,發布成制品,然后發布到生產環境。這些都是自動化的,流程化的。
CI/CD 工具有很多:Jenkins(比較古老了)、GitLab CI/CD、GitHub Action、Docker(發布制品) 和 k8s(容器編排)等。
前面說的 git hook,在本地 commit 時進行一些操作,也算是一種簡單的自動化。
打包工具
前端工程化的核心是打包工具。
打包工具需要支持的 幾種重要的能力:
- 代碼分割:指的將代碼劃分為可以按需 / 同時加載的多個bundles 或組件的能力。比如動態 import、提取公共依賴模塊代碼、多個入口文件沒有重復代碼、支持 ESM 的值引用模擬等。
- 哈希:資源更新時做哈希,防止資源緩存。哈希分很多種,比如文件路徑名哈希、內容哈希等。
- 包引入:ES Module、CommonJS 以及從 node_modules 目錄引入包的支持。
- 非 JS 資源:導入非 JS 資源的支持,像是 webpack 需要使用各種 loader 來支持,有些打包工具是內置的。
- 輸出的模塊格式:支持導出為 ES Module、CommonJS 等模塊。
- 轉換處理:比如對圖片壓縮、代碼壓縮、JS 版本降低等,在 webpack 中是使用 plugins 來實現的。
其他
還有一些零散的可以提高效率的工具。
- babel:開發時使用高版本的 ES 語言特性,然后生產環境用 babel 轉換為低版本的兼容性好的 ES5。打包工具內部其實使用了 babel。
- tsc:tsc(TS 編譯工具) 在支持 TS 的前提下,也支持編譯為 JS 低版本。
- polyfill:低版本的 ES5,想要使用一些新的 API,可以自己寫函數去模擬,這就是 polyfill,通常我們會使用 core.js 庫,但有些語言層面上的新特性就不能用 polyfill。
- monorepo:將多個項目放到一個 git 倉庫下,方便包的依賴共用和維護。
- 異常監控:當前端報錯時,將相關信息提交到異常監控服務,比如 sentry,通常配合 sourcemap 精確定位源碼中的錯誤位置。
- 制品庫:使用 Nexus 來部署自己的私有制品庫,支持各種制品庫。比如發布 docker 包、npm 包等,配合發版部署;
- VSCode Snippet:自定義 VSCode 編輯器的代碼片段,可以快速生成一些預置好的代碼模板,減少一些模板代碼的書寫。可以歸為自動化。
- Mock:前端在后端確認返回數據結構后完成接口前,可以通過模擬虛假數據進行調試開發,比如 yapi 平臺就是除了支持接口文檔,還提供 mock 功能。
- 單元測試:以模塊(比如組件)為單位進行測試,保證代碼邏輯符合預期。單元測試通常比較耗時,會在提交到遠端時或合并到主分支時進行。流行的單元測試庫有 Jest。
- 熱重載:因為每次改代碼都要編譯,如果整個項目都要重新編譯開發體驗很差,可以用熱重載只編譯被修改的模塊。
- 組件庫文檔:可以用 stroybook。如果是 vue 組件,可以考慮用 Vue Press。
- Tree shaking:丟掉一些引入了但沒有使用的模塊。
結尾
簡單來說,前端工程化是對前端開發流程的改良,是效率工具。