本系列已集結成書從 0 到 Webpack:學習 Modern Web 專案的建置方式,這是一本完整介紹 Webpack 的專書,如有學習 Webpack 相關的問題可以參考此書。
本文介紹 webpack 運作流程、以及說明為什麼使用 webpack 可以解決現代前端工程所遇到的問題。
讀者在看過前面的 JavaScript 模組化之路及新技術崛起兩篇文章後,應該都會發現現代前端工程急於出現一個可以整合眾多語法及技術的建置工具,因此許多的解決方案,像是 Browserify 、 Grunt 、 Gulp 、 Webpack 及 Rollup 等被發明。而其中, webpack 靠著龐大且完整的生態系成為大部分專案所使用的建置工具。
接下來我們就來一窺 webpack 的真面目吧。
在 webpack 中,任何的東西都被當作模組,所以 .js
、 .css
、 .png
、 .svg
...等各種檔案在 webpack 內都是一個個的模組。
在 JavaScript 模組化之路中有介紹到模組化編程的好處是各個模組的細節封裝,只需依靠介面對外擴展。因此在每個資源都被視為模組的 webpack 中,整個建置流程就不需了解資源的內部實作方式,只需要知道模組介面如何輸入、匯出即可,大大的簡化了複雜的建置流程。
將萬物皆模組的概念放在心上, webpack 的學習就變得十分簡單了。
接著我們來看看 webpack 是如何建置這些模組的吧。
webpack 是 JavaScript 應用程式的模組打包器。
它將各模組間的相依關係繪製成相依圖(dependency graph),依照相依圖解析並處理每一個模組,最後建置成一個或多個 bundle 。
webpack 的最終目標是將模組打包成 bundle ,其運作流程如下:
在運作流程的每個步驟都有事件鉤子,這些鉤子會觸發並執行對應的 Plugins 。
webpack 從起始點開始往下找尋相依模組,當所有的模組都被解析完成後就會輸出 bundles 並且輸出在目標資料夾中。
說到這裡,大家可能會對一些 webpack 內像是 Entry, Output, Loaders, Plguins, Mode, Bundle 等的名詞感到困惑,接下來將帶大家來了解這些名詞所代表的意義。
開發者要跟 webpack 說明哪個模組是建置相依圖時的起點,因此需要配置 entry
屬性。
啟動 webpack 時,會先解析起始模組,找出起始模組是否有相依其他的模組,接著再往下一個模組搜尋,一直到找不到相依模組時,如此一來就形成了相依圖。
entry
的預設值為./src/index.js
。
在 webpack 建立 bundle 時,他會需要知道 bunlde 要放在哪個路徑以及每個 bundle 要取什麼名字,因此我們需要設定 output
屬性。
output
的預設路徑是./dist
,而主要的 bundle 名稱會是./dist/main.js
。
webpack 本身只看得懂 JavaScript 與 Json 檔,對於其他的模組,需要借助 Loaders 幫忙解析。
因此當你引入這些 webpack 不認識的檔案(EX: .png
, .css
...等)時, webpack 就會嘗試去尋找合適的 Loaders 來載入這些資源,而對應的 Loaders 會將其轉譯成 webpack 讀得懂的模組,讓 webpack 可以將此模組加進相依圖內,並且繼續建置的工作。
webpack 在建置的過程中,會依序觸發不同的事件鉤子,藉以完成各個時期的工作 ,而 Plugins 可以藉著這些事件鉤子執行其所設定的工作。
Plugins 使得 webpack 有了更強大的能力,小如建置前清空輸出資料夾、注入環境變數、產生 html 檔案,大如配置最佳化等,都與 Plugins 有關係。
webpack 在建置時會依照 mode
設定的不同而進行不同的最佳化設定:
production
: 以生產環境為目標,做 Tree Shaking, Minify...等以執行效能為導向的最佳化development
: 以開發環境為目標,做 Source Map 等以開發便利為導向的最佳化
mode
預設為production
。
Module 是工程師所撰寫的模組,這些模組進入 webpack 建置流程中經過一系列的處理會轉變為 Chunk ,而最後做輸出處理後呈現在輸出目錄中的叫做 Bundle( 在某些地方稱為 Asset)。
可以將它們都想成是程式區塊,在建置過程中這些區塊可能會經過拆解、重組的步驟,因此才需要在各個時期有不同的名稱表示。
當你理解了整個 webpack 的運作流程後,就知道 webpack 已經完美的解決前兩篇文章所提到的問題: JavaScript 的模組化問題及新技術載入的問題。
webpack 為了可以產生 bundle ,它會解析模組與模組間的相依關係,而 webpack 會將下面語意都視為相依於其他模組:
import
require()
define
與 require
@import
與 url(...)
(需要 loader)<img src=...>
(需要 loader)可以看到使用 CommonJS, AMD 及 ES Module 的模組在 webpack 中都是合法的,因此只要使用了 webpack ,我們不需要特別處理模組相容的問題, webpack 已經幫我們做完了。
新的語言、預處理器或是框架通常都需要轉換成標準的 HTML, CSS 及 JavaScript 才能被瀏覽器執行,而 webpack 的 Loaders 的作用就是轉換各模組成為 webpack 能理解的模組,當 bundle 被建置出來時,已經是瀏覽器可以讀懂的檔案了。
因此你只要將新的技術引入 webpack 的建置流程中並且配置對應的 Loaders ,問題就解決了。
webpack 的核心目標是打包 JavaScript 應用程式的模組,它把所有的資源都視為模組。
從 Entry 模組開始尋找相依模組以建立相依圖,遇到非 JavaScript(JSON) 的檔案就交給 Loaders 幫忙轉換,最後建立 bundle 檔案並輸出在 Output 路徑下,而這個 bundle 是可以直接被瀏覽器載入。
並且可以使用 Plugins 賦予 webpack 除了打包外的其他能力,例如說 bundle 建置最佳化。
webpack 利用了整個打包流程解決了現代前端工程的模組化及新技術的引入問題,並且還可以做代碼的優化、檢查、分割...等等的處理,使得開發的應用程式層級可以向上提升。