Webpack 入門

白色風車發表於2019-03-27

隨著 Web 技術的發展,Web 應用變得越來越龐大複雜。直接編寫 HTML、CSS、JavaScript 開發 Web 應用的方式已經無法應對當前 Web 應用的發展。為了更好地開發和維護 Web 應用,JavaScript 社群制定了一系列的模組化規範幫助我們在前端世界裡進行模組化開發,模組化是指將一個複雜的系統分解為多個模組以方便開發和維護,其中比較著名的規範有 CommonJS 、AMD、ES6 Module。然而不管是哪一種規範,都不能直接在瀏覽器中執行。CommonJS 需要轉換成 ES5,AMD 需要有相應的模組 API 的實現(RequireJS是一個不錯的選擇),而 ES6 Module 雖然是 ECMA 制定的標準,但並非所有的瀏覽器都實現了,所以在使用時還是需要轉換。除此之外,各種新語言、新框架層出不窮,這些新技術帶來了各種各樣的語法,為了能讓它們執行在瀏覽器環境中,也需要使用特定的規則將它們轉換成瀏覽器能直接執行的程式碼。

完成這個轉換的過程就叫做構建。構建任務將這一系列的轉換過程用程式碼去實現,讓轉化過程自動化地執行,讓我們能更愉快地使用新技術。構建通常包括以下內容:

  • 程式碼轉換:將 Vue 單檔案模組編譯成 JavaScript,將 SCSS 編譯成 CSS 等。
  • 檔案優化:壓縮圖片和程式碼。
  • 程式碼分割:抽取公共程式碼非同步載入,節省流量,提高載入速度。
  • 模組合併:整合相互依賴的各個模組。

Webpack 是一個功能強大的構建工具,它能處理幾乎所有的檔案,對於 Webpack 來說,所有型別的檔案都是模組,你除了可以引入 JavaScript 模組外,還能把 CSS、圖片等型別的檔案當作模組引入使用。這樣的好處是能清晰地描述各個模組間的依賴關係,經過 Webpack 的處理,最終會輸出能在瀏覽器中執行使用的靜態資源。

通常我們通過檔案 webpack.config.js 來配置 Webpack,該檔案告訴 Webpack 專案入口在哪裡,該如何處理不同型別的模組,最終生成的結果存放在哪裡。一個簡單的配置檔案示例如下:

const path = require( 'path' );

module.exports = {
  entry : './main.js',

  output : {
    filename : 'app.js',
    path     : path.resolve( __dirname, './dist' )
  }
};

如你所見,這個配置檔案只是簡單地返回一個物件,其中 entry 指定了專案的入口,Webpack 會從這個檔案開始,識別出程式碼中的模組匯入語句,遞迴地找出入口檔案的所有依賴模組,並將這些模組打包到一個單獨的檔案中。這個新的檔案的配置資訊通過 output 來定義,在上面的例子中,該檔案的檔名為 app.js,存放在 dist 目錄下。

前面我們提到過,Webpack 把一切檔案都看作模組,我們可以想引入 JavaScript 模組一樣引入 CSS,只需要 require( '[css-file-name].css' ) 就可以了。但是這樣引入後直接執行 Webpack 構建任務是會出錯的,Webpack 並不能直接解析 CSS 檔案,我們需要 Loader 來幫助處理 CSS。

module.exports = {
  module : {
    rules : [ {
      test : /\.css$/,
      use  : [ 'style-laoder', 'css-loader' ]
    } ]
  }
};

Loader 就是 Webpack 所使用的模組轉換器,它把特殊的模組轉換成 JavaScript 檔案。上面的例子告訴 Webapck,使用 style-loader 和 css-loader 來處理所有以 .css 結尾的檔案。其實 loader 並沒有大家想象的那麼複雜,css-loader 將 css 檔案中的樣式以字串的形式存起來,style-loader 在執行時通過向 html 注入 style 標籤的形式將字串注入。就是這麼簡單,你應該發現了,這兩個過程是存在先後順序的,通過 use 定義的 loader 的執行順序是從後往前執行,不要忘記了哦。

Webpack 的生態非常豐富,提供了各種各樣的 Loader,總有一款能滿足你的需求。除此之外,Webpack 還提供了鉤子,讓我們在構建流程中的特定時間自定義一些功能。聽起來很麻煩?不要擔心,你可以直接使用現成的 Plugin。舉個例子,對於 Webpack 生成的結果,我們需要手動引入到 html 檔案中才能使用,這是不是很麻煩呢?還好我們有 HtmlWebpackPlugin,它能根據我們的配置自動實現這個步驟。

module.exports = {
  plugins : [ new HtmlWebpackPlugin( {
    filename : 'app.html',
    hash     : true,
    template : 'app.html'
  } ) ]
};

每個 Loader 和 Plugin 的具體用法可以參考對應的文件,這裡就不在贅述。

Webpack 的使用就是這麼簡單,只需要按照自己的需求配置相應的選項,就能愉快地玩耍啦~

最後總結一下,Webpack 啟動後會從配置檔案的 entry 中讀取入口,然後從入口開始遞迴地解析依賴,每找到一個依賴,就根據配置的 Loader 去找出對應的轉換規則並進行轉換,轉換完成後,再解析出當前模組的依賴,重複上述過程。這些依賴會根據 entry 分組,一個 entry 依賴的模組分為一組,稱為一個 chunk,chunk 最後被轉換成檔案輸出。在整個流程中,Plugin 定義的邏輯會在適當的時候執行,這主要取決的 Plugin 使用了 Webpack 的哪些鉤子。

相關文章