Webpack 的簡單介紹

暖生發表於2019-01-30

本文介紹了一些 webpack 的核心概念以及一些概念術語,並對核心配置做了一些簡單的用法講解。建議剛剛接觸 Webpack 的朋友可以先了解一下。想了解更多 Webpack 使用以及配置的話可以參考我的下一篇文章: 從零開始搭建一個 Webpack 開發環境配置(附 Demo)


為什麼需要使用 webpack

  • 模組化開發的趨勢 我們在開發的過程中,之前都是使用引入 script 的方式進行各種工具和外掛的引入,但是這樣會造成很大程度上的全域性汙染,所以引入了 模組化 的概念,但是不管是 commomJs 還是 CMD,AMD 的方式,瀏覽器都無法識別,而使用 webpack 就可以自動的將檔案編譯成瀏覽器可以識別的程式碼

  • less、sass 以及 ES6 語法的使用 同樣的,直接使用 less、sass 和 ES6 語法,直接使用,瀏覽器甚至 node 環境也無法正常識別,所以這時候 loader 就派上用場了,可以使用 less-loader、sass-loader 以及 babel-lodaer 對對應的檔案進行轉換之後,就可以正常的編譯了

  • 監聽檔案的變化並自動重新整理網頁,做到實時預覽

  • 提供 HTTP 服務而不是使用本地檔案預覽

  • 對於打包後的檔案進行壓縮,模組拆分,減小打包後的檔案體積

  • 等等還有很多別的優勢,這裡我就不一一列舉了


webpack 安裝

  • 全域性安裝:

    npm install --global webpack
    複製程式碼
  • 本地安裝:

    npm install --save-dev webpack
    npm install --save-dev webpack-cli
    複製程式碼

Webpack 概念

webpack 是一個可高度配置的現代 JavaScript 應用程式模組打包器。當 webpack 處理應用程式時,它會遞迴地構建一個依賴關係圖,其中包含應用程式需要的每個模組,然後將所有這些模組打包成一個或多個 bundle。

Webpack 核心概念

入口

入口點可以告訴 webpack 從哪裡啟動以及遵循依賴關係圖,以此知道要打包什麼東西。你可以考慮將待打包檔案的根目錄作為你應用程式的入口點。

個人理解:入口就是 webpack 在處理應用程式時,需要知道從哪個檔案開始執行,這個檔案就是整個程式的入口檔案。

用法:entry: string|Array|Object

基本用法

webpack.config.js

const config = {
  entry: './src/index.js'
};

module.exports = config;
複製程式碼

陣列語法

一般適用於多頁面應用,多個入口的場景。

webpack.config.js

const config = {
  entry: ['./src/entry1', './src/entry2']
};

module.exports = config;
複製程式碼

物件語法

一般適用於分離 應用程式(app) 和 第三方庫(vendor) 入口。

webpack.config.js

const config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
};

module.exports = config;
複製程式碼

配置動態入口

假如專案裡有多個頁面需要為每個頁面的入口配置一個 Entry ,但這些頁面的數量可能會不斷增長,則這時 Entry 的配置會受到到其他因素的影響導致不能寫成靜態的值。

webpack.config.js

// 同步函式
entry: () => {
  return {
    a:'./pages/a',
    b:'./pages/b',
  }
};
// 非同步函式
entry: () => {
  return new Promise((resolve)=>{
    resolve({
       a:'./pages/a',
       b:'./pages/b',
    });
  });
};
複製程式碼

輸出

上面既然已經有入口檔案了,那麼 webpack 執行了一系列操作之後,生成的新的打包後的檔案應該放到哪兒呢,所以我們需要指定一下。

在 webpack 中配置 output 屬性的最低要求是,將它的值設定為一個物件,包括以下兩點:

  • filename 用於輸出檔案的檔名
  • 目標輸出目錄 path 的絕對路徑

基本用法

webpack.config.js

const path = require('path');

const config = {
    entry: './src/index.js',
    output: {
        // 最基本的兩個配置要求
        filename: 'bundle.js',  // 輸出檔案的檔名
        path: path.resolve(__dirname, 'dist')   // 目標輸出目錄 path 的絕對路徑,這裡必須是絕對路徑
    }
};

module.exports = config;
複製程式碼

多個入口起點

如果配置建立了多個單獨的 "chunk",則應該使用佔位符(參考*內建的佔位符變數*)來確保每個檔案具有唯一的名稱。

webpack.config.js

const path = require('path');

const config = {
  output: {
    // 最基本的兩個配置要求
    filename: '[name]-bundle.js',  // 輸出檔案的檔名
    path: path.resolve(__dirname, 'dist')   // 目標輸出目錄 path 的絕對路徑
  }
};

module.exports = config;
複製程式碼

內建的佔位符變數

變數名 含義
id Chunk 的唯一標識,從0開始
name Chunk 的名稱
hash Chunk 的唯一標識的 Hash 值
chunkhash Chunk 內容的 Hash 值

loader

loader 用於對模組的原始碼進行轉換。loader 可以使你在 importrequire() 或"載入"模組時預處理檔案。 loader 可以將檔案從不同的語言(如 TypeScript)轉換為 JavaScript,或將內聯影像轉換為 data URL。loader 甚至允許你直接在 JavaScript 模組中 import CSS檔案!

基本使用

例如:載入 css 模組:

  • 安裝對應的 loader

    npm install --save-dev style-loader css-loader
    複製程式碼
  • 然後指示 webpack 對每個 .css 使用 css-loader 和 style-loader

webpack.config.js

``` js
module.exports = {
    module: {
        rules: [
            { test: /\.css$/, use: ['style-loader', 'css-loader' }
        ]
    }
};
```
複製程式碼

配置 loader

rules 配置模組的讀取和解析規則,通常用來配置 Loader。其型別是一個陣列,陣列裡每一項都描述瞭如何去處理部分檔案。 配置一項 rules 時大致通過以下方式:

  • 條件匹配:通過 test 、 include 、 exclude 三個配置項來命中 Loader 要應用規則的檔案

  • 應用規則:對選中後的檔案通過 use 配置項來應用 Loader,可以只應用一個 Loader 或者按照從後往前的順序應用一組 Loader,同時還可以分別給 Loader 傳入引數

  • 重置順序:一組 Loader 的執行順序預設是從右到左(從下到上、從後到前)執行,通過 enforce 選項可以讓其中一個 Loader 的執行順序放到最前或者最後

  • 在 Loader 需要傳入很多引數時,你還可以通過一個 Object 來描述

  • test include exclude 這三個命中檔案的配置項可以傳入一個字串或正則,其實它們還都支援陣列型別

具體使用:

module.exports = {
    module: {
        rules: [
            
             {
                // 命中 JavaScript 檔案
                test: [
                    /\.js?$/,
                    /\.jsx?$/
                ],
                // 用 babel-loader 轉換 JavaScript 檔案
                // ?cacheDirectory 表示傳給 babel-loader 的引數,用於快取 babel 編譯結果加快重新編譯速度
                use: [
                    {
                        loader:'babel-loader',
                        options:{
                            cacheDirectory:true,
                        },
                        // enforce:'post' 的含義是把該 Loader 的執行順序放到最後
                        // enforce 的值還可以是 pre,代表把 Loader 的執行順序放到最前面
                        enforce:'post'
                    }
                ],
                // 只命中src目錄裡的js檔案,加快 Webpack 搜尋速度
                include: path.resolve(__dirname, 'src')
            },
            {
                // 命中 SCSS 檔案
                test: /\.scss$/,
                // 使用一組 Loader 去處理 SCSS 檔案。
                // 處理順序為從後到前,即先交給 sass-loader 處理,再把結果交給 css-loader 最後再給 style-loader。
                use: ['style-loader', 'css-loader', 'sass-loader'],
                // 排除 node_modules 目錄下的檔案
                exclude: path.resolve(__dirname, 'node_modules')
            }
        ]
    }
};
複製程式碼

plugins

Plugin 是用來擴充套件 Webpack 功能的,通過在構建流程裡注入鉤子實現,它給 Webpack 帶來了很大的靈活性。

基本使用

比如每次打包檔案到 dist 資料夾下,就可能導致資料夾下面的檔案過多,內容過大,那麼此時我們可能需要在打包之前先將 dist 檔案下的檔案都刪除掉,但是每次都手動刪除豈不是太麻煩,此時我們就可以使用 clean-webpack-plugin 外掛來幫助我們清理 dist 資料夾

外掛安裝

npm install clean-webpack-plugin --save-dev
複製程式碼

webpack.config.js

const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    plugins: [
        new CleanWebpackPlugin(['dist'])
    ]
}
複製程式碼

其他概念術語

資源(Asset)

這是一個普遍的術語,用於圖片、字型、媒體,還有一些其他型別的檔案,常用在網站和其他應用程式。這些檔案通常最終在輸出(output ) 中成為單個檔案,但也可以通過一些東西內聯,像 style-loader 或者 url-loader 。

Bundle

由多個不同的模組生成,bundles 包含了早已經過載入和編譯的最終原始檔版本。

Chunk

這是 webpack 特定的術語被用在內部來管理 building 過程。bundle 由 chunk 組成,其中有幾種型別(例如,入口 chunk(entry chunk) 和子 chunk(child chunk))。通常 chunk 會直接對應所輸出的 bundle,但是有一些配置並不會產生一對一的關係。

依賴關係圖(Dependency Graph)

有時候一個檔案依賴於其他檔案,webpack 將其視為依賴關係(dependency)。從一個或多個入口點開始,webpack 遞迴構建一個依賴關係圖,裡面包含了你的應用程式需要的所有模組/資源(mudule/asset)。

模組(Module)

提供比完整程式接觸面(surface area)更小的離散功能塊。精心編寫的模組提供了可靠的抽象和封裝界限,使得應用程式中每個模組都具有條理清楚的設計和明確的目的。

  • 模組解析(Module Resolution):一個模組可以作為另一個模組的依賴模組,resolver 是一個庫( library )用於幫助找到模組的絕對路徑... 模組將在 resolve.modules 中指定的所有目錄內搜尋。

Tree Shaking

移除未使用/多餘的程式碼,或者更準確地說,只匯入引用的程式碼。編譯器(compiler)(例如 webpack)將通過分析各種 import 語句和引入程式碼的使用情況,來確定哪些部分的依賴關係被實際使用,刪除不是“樹”的部分,以實現此功能

第三方庫入口點(Vendor Entry Point)

從 app.js 和 vendors.js 開始建立依賴圖(dependency graph)。這些依賴圖是彼此完全分離、互相獨立的,允許你使用 CommonsChunkPlugin 從「應用程式 bundle」中提取 vendor 引用(vendor reference) 到 vendor bundle。可以幫助你在 webpack 中實現被稱為長效快取的通用模式。

優化

  • Bundle 分離(Bundle Splitting) 這個流程提供一個優化 build 的方法,允許 webpack 為應用程式生成多個 bundle。最終效果是,當其他某些 bundle 的改動時,彼此獨立的另一些 bundle 都可以不受到影響,減少需要重新發布的程式碼量,因此由客戶端重新下載並利用瀏覽器快取。

  • 程式碼分離(Code Splitting) 指將程式碼分離到每個 bundles/chunks 裡面,你可以按需載入,而不是載入一個包含全部的 bundle。


總結

本文簡單介紹了一下 Webpack 的作用以及 Webpack 核心概念的用法。想了解更多的朋友可以參考我的下一篇文章:從零開始搭建一個 Webpack 開發環境配置(附 Demo)