前端勸退預警:JavaScript 工具鏈不完全指南
阿新 • • 發佈:2020-03-11
![宇宙中最重的物質](https://user-gold-cdn.xitu.io/2020/3/11/170c7474bca121fb?w=958&h=576&f=png&s=232767)
經過這麼多年的發展,JavaScript 早已經不是當年那個不太起眼的指令碼語言。如今的 JavaScript 可以說是風光無限,在 Web 前端、移動端、服務端甚至物聯網裝置上都大展身手,到處都有它的身影。
在 JavaScript 語言日漸強大的同時,與其配套的開發工具也蓬勃發展。現在的 Web 前端專案,早已不是寫幾個 HTML 頁面,加點 CSS 和 JS 就完事了。隨便一個實用的專案,可能都需要用到一些框架和第三方庫,以及相應的腳手架、依賴包管理、預編譯、構建打包、壓縮合並等等工具。純手工完成這些任務,已經幾乎不太可能了。
科學技術是第一生產力,而工具就是其中的一個體現。工欲善其事必先利其器,既然工具解放了人力,我們就應該擁抱它們。本文總結了圍繞 JavaScript 的一系列工具,看看一個常見的專案到底需要用到哪些工具。
### 靜態型別檢查
JavaScript 本身是一門動態指令碼語言,是弱型別的。也就是說,沒有編譯階段的資料型別檢查,只能在執行時確定型別。好處是比較靈活,簡單易學,程式碼量也比較少。缺點也是明顯的,就是稍不注意容易出 bug,特別是大型專案,如果沒有開發規範,任由開發人員自由發揮,維護起來簡直就是災難。為了彌補這個缺陷,一些自帶型別系統、可轉譯成 JavaScript 的語言出現了。
**[Flow](https://flow.org/en/)**:Facebook 出品,用 OCaml 寫的 JavaScript 靜態型別檢查系統。支援型別系統、型別標註,可定義 library,提供程式碼 lint 等。Vue.js 2.x 就是用 Flow 寫的。不過,Vue 3.0 改用另一種靜態型別語言了,那就是 TypeScript。
**[TypeScript](https://www.typescriptlang.org/)**:TypeScript 是微軟開發的語言,是 JavaScript 的超集,能夠編譯成 JavaScript。現在 TypeScript 越來越流行,獲得了大量專案的認可。
### 程式碼風格檢查(Linter)
由於JavaScript 動態語言的特性,在寫法上過於靈活,往往導致多人協作的專案程式碼風格各異,給維護和擴充套件帶來不少麻煩。另外,部分語言特性容易導致 bug,最佳實踐裡通常不推薦使用。因此,程式碼 Lint 工具就派上用場了。它不但可以檢查程式碼書寫格式,還可以檢查出引用未定義的變數等低階錯誤。
曾經出現過很多種 Lint 工具,比如 JSLint、JSHint、StandardJS、JSCS 等等,現在有大一統的趨勢,基本都用 ESLint 了。
剛開始用 Lint 工具的時候可能會不適應,因為限制太多,動不動就警告和報錯。但從長遠來看,JavaScript 專案引入 Linter 還是有必要的。
### 包管理器
JavaScript 之所以能夠遍地開花,很大程度是因為技術生態非常繁榮,各種第三方庫應有盡有。如此龐大的第三方庫集合,勢必需要一個管理平臺,負責第三方包的託管、版本管理、下載安裝和依賴管理等。
**npm:**JavaScript 包管理器的集大成者,目前最主流的工具。 基於 Node.js,包含網站和 CLI,上面的包總數超過 100萬,基本涵蓋了 JavaScript 專案所需的方方面面。
**yarn**:2016 年 Facebook 推出的包管理器,跟 npm registry 相容,主打 CLI 的快速、安全和確定性。
**bower**:曾經比較流行,用於管理前端的 JavaScript 包,因為當初 npm 只支援 node 環境的包管理。隨著 npm 和 yarn 同時支援 node 和瀏覽器端的包管理,bower 也逐漸淡出歷史了。
**pnpm**:通過硬連結(hard links)的方式在硬碟上對某個版本的模組只儲存一份,可以在多個專案之間共用,從而節省了大量硬碟空間,順便也加快了模組安裝速度。
### 模組載入器
JavaScript 早期沒有語言層面的模組化支援,導致大型專案的依賴管理非常不方便。變數名衝突、全域性變數汙染、模組載入順序等問題比較突出。曾經出現過各種模組化方案,比如 CommonJS Modules (CJS),Asynchronous Module Definition (AMD)和 Universal Module Definition (UMD)。從 2015 年開始,ES6 從語言層面支援了模組化,即 ECMAScript Modules (ESM)。
除了上述常見的 JavaScript 模組格式,還有像 [System.register](https://github.com/systemjs/systemjs/blob/master/docs/system-register.md) 或全域性模組,以及一些非 JavaScript 模組,比如 JSON modules,CSS modules,Web Assembly 等。
模組載入器就是載入和處理上面各種型別模組程式碼的工具,有同步和非同步、靜態和動態之分。通常,一個模組放在一個單獨的檔案裡,通過一些指令來匯入。當一個專案有幾十上百個模組的時候,如何保證它們的載入執行先後順序就是個問題。不用擔心,模組載入器會幫你處理好依賴關係。
**[RequireJS](https://github.com/requirejs/requirejs):**實現了 AMD 模組載入,主要用於瀏覽器端,也可以在 Node 裡使用。
**[SystemJS](https://github.com/systemjs/systemjs)**:一個動態模組載入器,可以載入所有型別的 JavaScript 模組,甚至包括非 JavaScript 模組,可用於瀏覽器和 Node。
**[StealJS](https://github.com/stealjs/steal/)**:可以載入 ESM,ADM 和 CJS 格式的模組。
**ES Module Loader**: [瀏覽器](https://caniuse.com/#search=modules)實現的模組載入器,Node.js 里加上 `--experimental-modules` 標誌也能使用。
以上模組載入器是在**執行時**載入和執行程式碼的。不要跟構建工具裡的各種 loader 混淆,那些是在**構建時**進行預處理的工具,只是做一些格式轉換。
### 打包工具
一個實際的生產專案,通常不是把原始碼直接釋出到伺服器上。而是通過一些工具對各種模組和靜態資源進行整合處理,最終生成的程式碼可能跟原始碼完全不一樣了。這就是打包工具的作用。比較常見的打包工具有:
**Webpack**:可以說是最流行的打包器,幾乎是大部分專案的標配。它本身只能識別 JavaScript 和 JSON 檔案,但是它的架構設計提供了無限的可能,通過各種 loader 可以處理任意型別的資源,通過外掛可以優化打包結果和進行資源管理、注入環境變數等。它的構建目標也可以根據平臺定製,比如瀏覽器、Node.js、web worker, Electron 等。
**Rollup**:也是一個優秀的打包器,支援輸出 library 和 application。它最大的賣點就是預設支援 ES 模組,很早就實現了 tree-shaking 功能。同時也具備外掛和 hook 功能,可定製化也比較好。
**Parcel**:工具界的後起之秀,號稱“零配置*的打包器,如果你曾經被 Webpack 繁瑣的配置困擾過,那它可能會吸引你。
**Browserify**:應用範圍稍窄的打包器,專門用於轉換 Node.js 包以便能在瀏覽器執行。它跟 Node.js 使用相同的模組系統,有些模組只用於 Node 平臺,通過它的轉換就可以在瀏覽器端使用了。它只能處理純 JavaScript 模組,通常跟 Gulp 配合使用。
**Metro**:React Native 專用的打包器,通過一個入口檔案和各種配置,最後打成包含所有程式碼和依賴的單個 JavaScript 檔案。
### 任務管理工具(Task Runner)
Task Runner 的作用是自動化執行專案所需的各種重複性動作,比如:
- CSS 預處理 (Less, Sass)
- CSS 自動新增新特性屬性字首(Autoprefixer)
- 優化圖片
- 合併、壓縮 JavaScript 檔案
- 監聽檔案變化,自動執行任務
等等。
比較主流的工作管理員是 Grunt 和 Gulp,Webpack 也算一個。
**Grunt**:命令列工具,通過精細化的配置和豐富的外掛可以完成很多複雜的任務。
**Gulp**:跟 Grunt 不同,Gulp 採用流式管道組合多個任務,任務之間的臨時結果是放在記憶體中的,執行效率會高很多。
**Webpack**:又是你,Webpack。任務管理只是 Webpack 強大功能的一部分,通過不同的外掛在構建的不同階段執行個性化的任務。
如果你覺得上面幾個工作管理員有點殺雞用牛刀,你也可以根據實際情況選用 bash 指令碼,npm 指令碼或者 Makefile 等實現一些簡單的任務管理。
### 轉譯器
JavaScript 轉譯器的作用是將非 JavaScript 語言(TypeScript,CoffeeScript,LiveScript 等)或不同版本的 JavaScript(ES6,ES7,ES8 等)翻譯成符合目標平臺要求(相容性、變數混淆、嚴格模式等)的等價程式碼。
大部分轉譯器在處理原始碼和程式碼優化的過程中都使用抽象語法樹(AST)作為中間格式。AST 將原始碼逐步拆分解析成帶有元資料的樹形結構:
```
Code --(parse)-->AST--(transform)-->AST--(generate)-->Code
```
[Babel](https://babeljs.io/) 是目前主流的 JavaScript 轉譯器,它的工具鏈體系主要用於將 ES6 以上版本的 JavaScript 轉譯成向後相容瀏覽器的程式碼。Babel 可以轉換語法、提供目標平臺缺失的特性支援、轉換原始碼等。
有了 Babel,我們可以儘早用上新的語言特性,而不用擔心目標瀏覽器是否支援。
### 構建工具
構建其實是個綜合概念,它包含了模組打包、原始碼轉譯、任務管理等多個步驟。其他語言和平臺有各種 Build 工具,比如 Make,Gradle,Ant,Maven,Rake 或 MSBuild 等。其中 Make 是最常見的通用構建工具,通常用於 C 語言,但其實也可以用於構建 JavaScript 專案。
對於 JavaScript 工具鏈來說,構建的目標可能是 npm 包、網站、Node 伺服器、RN 應用、Electron 應用等等。有些任務可用專門的工具完成,但構建是個複雜的過程,通常需要綜合使用多個工具。
比如 Build 可以用 [Buck](https://buck.build/),[Bazel](https://bazel.build/),[Lerna](https://lerna.js.org/) 等工具同時管理多個模組,實現增量 Build(只重新構建有改動的模組,提高效率)。任務管理使用 Grunt 和 Gulp,模組打包用 Webpack、Rollup、Parcel、Browserify 等。
### 除錯工具
偵錯程式在開發過程中必不可少,它可以在 Node.js 和瀏覽器中跟蹤檢視執行中的程式碼。通常會提供斷點、單步執行、監視變數、檢視記憶體和 CPU 使用情況等功能。
**[Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools)**:Chrome 瀏覽器自帶的開發者工具,是目前瀏覽器開發工具裡最好用的(沒有之一)。它的功能之強大,用法之多可以寫本書了。
**[node-inspector](https://github.com/node-inspector/node-inspector)**:早期用於除錯 Node.js 程式碼的工具,現在基本不用了,因為 Node.js 已經內建了基於 DevTools 的偵錯程式。
**[VS Code](https://vscode.readthedocs.io/en/latest/editor/debugging/)**:又一除錯神器,內建 Node.js 偵錯程式,還能除錯 TypeScript 和其他能轉譯成 JavaScript 的語言。
### Node 程序管理器
Node 程序管理器用於管理執行中的 Node 應用程式。提供高可用、自動重啟、檔案監控、效能和資源監控和叢集等功能。
**[Forever](https://github.com/foreverjs/forever)**:顧名思義,它的作用是保持 Node.js 程式永遠執行。它是一個簡單的命令列工具,對小型 Node.js 應用比較方便。
**[PM2](https://github.com/Unitech/pm2)**:用於生產環境的 Node.js 程序管理工具,內建負載均衡、自動重啟和日誌、監控和叢集管理等功能。
**[StrongLoop Process Manager](http://strong-pm.io/) (Strong-PM)**:跟 PM2 差不多,也可用於生產環境的 Node.js 程序管理,有多主機部署功能。
另外,SystemD 是 Linux 系統裡的預設程序管理器,可用它把 Node.js 應用作為服務執行。
### 專案腳手架
隨著專案複雜度的提升,建立新專案的步驟也變得越來越繁瑣,可能需要重複大量的配置工作。這個時候就需要專案腳手架了。很多框架都提供了腳手架工具,比如 Vue.js 有 Vue CLI,Angular 有 Angular CLI,React 有 create-react-app 等。也有通用的腳手架,Yeoman 就是最流行的一個。
- 快速建立新專案
- 建立模組或 package
- 啟動新服務
- 統一程式碼風格、最佳實踐等
- 專案推廣,讓使用者快速上手示例 app
- 等等等等等等,自己探索
### 總結
本文列舉了 JavaScript 常規專案可能要用到的工具鏈,零零散散幾十個。每一個工具都解決了特定領域的問題,值得繼續挖掘,本文由於篇幅所限只能蜻蜓點水式地一筆帶過,更多前端技術分享可以關注下我的公眾號[1024譯站]。
![微信公眾號:1024譯站](https://user-gold-cdn.xitu.io/2020/3/11/170c7474bf074ade?w=258&h=258&f=jpeg&