手摸手的教你配置一個基於webpack4的react開發環境

學生小甘發表於2018-12-10

前言

webpack作為現代前端技術的基石,相信絕大部分的前端開發者都聽說或是在專案中使用過。但是由於現在各種各樣cli工具的出現,能夠掌握webpack基本配置和使用的人,可能就不那麼多了。

最開始接觸webpack還是在以前的angular.js專案中,之後從angular.js轉到react後一直使用的create-react-app進行專案初始化。需要改動webpack配置時也是在使用eject命令暴露出webpack config進行修改,一直沒有自己從零開始配置過。在這期間angular.js升級成為Angular,jQuery逐漸被歷史淘汰,Vue也從一個後期之秀成為現在的當紅前端框架。不得不說,前端技術的更新迭代速度真的可以用飛速來形容了。在如此快速的更新速度下,作為一個前端開發人員,一定要緊跟技術的步伐,時刻保持著學習狀態,才能保證自己在大潮流中不掉隊。這也是我寫這篇文章的初衷,用來記錄自己的學習成果。

要注意的是,本文不會贅述現代前端開發中的一些基礎知識,如 npm依賴管理、模組化等基礎知識。所以,若你並沒有聽說過webpack,或不知道它是什麼,那麼建議你還是先了解一下基礎知識。若對webpack已經瞭如指掌,那麼也大可不必看這篇文章。不過大佬若是願意指導一番,我也是非常開心的!!^_^

正文

簡介

webpack是什麼?

本質上,webpack 是一個現代 JavaScript 應用程式的靜態模組打包器(static module bundler)。在 webpack 處理應用程式時,它會在內部建立一個依賴圖(dependency graph),用於對映到專案需要的每個模組,然後將所有這些依賴生成到一個或多個bundle。—— 來自 webpack 官方文件

簡單的說,webpack就是一個現代前端開發的打包機。現代前端非常流行使用模組化的方式開發,webpack所做的就是將各個js模組合理的打包成bundle或者chunk。在打包的過程中,可以通過loader的方式,將新的js語法、CSS預處理語言等轉換成更容易被瀏覽器支援的形式。

webpack是基於nodejs的,在絕大部分時,在使用時需要為它寫一個配置檔案。這個配置檔案的主要結構如下:

module.exports = {
    mode: 'development'      // 模式配置
    entry: '',               // 入口檔案
    output: {},              // 出口檔案
    module: {},              // 處理對應模組
    plugins: [],             // 對應的外掛
    devServer: {},           // 開發伺服器配置(僅在開發中使用)
}
複製程式碼

接下來,我們就來一步步的完成這些配置。

準備

初始化和安裝

在指定資料夾下執行 npm init 進行初始化。

mkdir webpackDemo&&npm init

因為專案並不是一個要釋出到npm的專案,所以執行npm init後只用一路回車即可。

安裝webpack和react的依賴:

npm install --save-dev webpack react react-dom

在webpack4之後的版本中,還需要安裝webpack-cli,具體方法同上。

建立初始目錄結構和檔案

在專案根目錄建立config資料夾,並在內建立webpack.config.js。

開啟根目錄下的package.json 配置scripts:

"scripts": {
    "build": "webpack --mode production --config ./config/webpack.config.js",
  }
複製程式碼

配置scripts指令碼是為了後期在執行過程中只用在命令列中輸入 npm '指令碼中指定配置' 就能夠完成命令列的輸入操作。比如輸入 npm build,就會自動執行 "webpack --mode production --config ./config/webpack.config.js" 這一長串的操作。

建立程式碼資料夾和react的入口檔案:

在專案根目錄中建立src資料夾,並在內建立index.js、App.js、index.css。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css'


ReactDOM.render(
  <App />,
  document.getElementById('root')
);
複製程式碼

App.js

import React from 'react';

export default class App extends React.Component {
    render() {
        return <div>
            <p className="text">動手搭建一個基於webpack4的react開發環境</p>
        </div>
    }
}
複製程式碼

index.css

.text{
    color:'red'
}
複製程式碼

完成上述操作後,專案目錄結構應該像下面這樣

webpackDemo
│   node_modules
└───config
    │   webpack.config.js
└───src
    │   index.js
    │   index.css
    │   App.js
    package.json
複製程式碼

現在,我們完成了簡單的初始化工作,下面開始瞭解webpack吧。

模式 (mode)

mode是webpack4中新增的概念,它有三個選項:developmentproductionnone,用來設定webpack的優化方式。

development

開發模式,該模式優化了開發速度,提供了詳細的錯誤機制和瀏覽器除錯工具。並且關閉了程式碼壓縮,使程式碼能夠更快的構建。

production

生產模式,該模式能夠提供更小的程式碼包,去除了只在開發階段執行的程式碼。自動開啟了程式碼混淆壓縮。

配置

module.export = {
  mode:'production' // 'development'||'production'||'none'
}
複製程式碼

程式入口 (entry)

在這裡,可以宣告一個應用的起點。入口可以有一個或者多個。在單頁應用中,入口一般只有一個。不過也可以將公共依賴配置成為單頁應用的入口,這樣單頁應用也可以有多個入口。而在多頁應用中,一般會有多個入口檔案。

一個簡單的單頁應用入口如下:


module.export = {
  mode:'production' // 'development'||'production'||'none',
  entry:'./src/index.js',
}

複製程式碼

輸出 (output)

output用來配置專案打包後的檔名稱、路徑。用來告訴webpack怎麼輸出、輸出到哪、叫什麼名字。

const path = require('path');

module.export = {
  mode:'production' // 'development'||'production'||'none',
  entry:'./src/index.js',
  output: {
    // 在bundle中引入註釋 注意:該選項不應該在生產模式中啟用
    pathinfo:true,
    // 所有輸出檔案的目標路徑
    // 必須是絕對路徑(使用 Node.js 的 path 模組)
    path: path.resolve(__dirname, './../build'),
    // 輸出的檔名配置
    filename: "[name].[hash].js"
    }
}
複製程式碼

這裡的filename並沒有給它一個實際的名稱,而是使用模板字串去設定webpack生成後的檔名稱。這個設定中的[name]代表模組名稱,在單入口檔案中預設為main。而[hash]則會生成一個模組識別符號的hash,預設是20位,可以通過[hash:16]的方式指定它的位數。打包後的檔名稱就像這樣main.f236aaeca342dfb1f8dd.js。在生成檔名稱後跟上hash有助於我們在專案重新部署後由於引用的檔名稱變了,瀏覽器會去下載新的檔案,不再繼續使用本地的快取。

loader

webpack的作用就是將前端開發中的各個模組進行處理以及打包。而loader的作用就是處理webpack中的這些模組。

webpack中模組有很多種,常見的有:

  • 模組化的js檔案

  • css/less/sass檔案

  • 圖片以及靜態檔案

loader在module中配置:

// 示例
const path = require('path');

const appSrc = path.resolve(__dirname, '../src')

module.exports =  {
    mode: 'development',
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },

    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,     // 用來指定針對的檔案型別 支援正則
                exclude: /node_modules/, // 用來指定需要排除的資料夾,優化打包速度
                include: appSrc,         // 指定所包含的資料夾 ,優化打包速度
                loader: "babel-loader", // 針對指定檔案使用的loader
            }
        ]
    }
};

複製程式碼

要對這些模組進行處理,就要使用到不同的loader。在此之前,先簡單的介紹一下需要使用到的loader。

babel-loader

babel是一個語法轉換器,能夠讓你自由的使用JavaScript的最新語法。它能夠將我們所寫的新語法、jsx等轉換成瀏覽器能夠友好支援的形式。

要使用babel-loader需要下列依賴,可以通過執行npm install --save-dev babel-loader @babel/core @babel/preset-react @babel/preset-env安裝它們。

  • babel-loader

  • @babel/core

    babel的核心元件,裡面包含著babel的api。

  • @babel/preset-env

    用來轉義JavaScript語法。

  • @babel/preset-react

    用來轉義react。

配置babel-loader:

const path = require('path');

const appSrc = path.resolve(__dirname, '../src')

module.exports =  {
    mode: 'development',
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    // 指定babel預處理轉義
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            }
        ]
    }
}
複製程式碼

完成上述配置後,還需配置一下babel,讓它能夠轉換react和js的新語法。可以像上面使用webpack配置中的option選項中的presets欄位指定babel預處理的方式。

也可以在專案的根目錄建立babel的配置檔案.babelrc.babelrc字尾rc來自linux中,使用過linux就知道linux中很多rc結尾的檔案,比如.bashrc,rc是run command的縮寫,翻譯成中文就是執行時的命令,表示程式執行時就會來呼叫這個檔案。

babel所有的操作基本都會來讀取這個配置檔案,除了一些在回撥函式中設定options引數的,如果沒有這個配置檔案,會從package.json檔案的babel屬性中讀取配置。

.babelrc中新增下列語句:

{
    "presets": ["@babel/preset-env","@babel/preset-react"]
}
複製程式碼

url-loader

url-loader和file-loader的作用類似,都是使webpack能夠打包靜態檔案。url-loader相較於file-loader的功能更強大,它能夠使用兩種方式進行打包。

url-loader有一個重要的引數 limit ,這個引數用來設定打包檔案大小的限制。當檔案小於指定引數時,它能夠返回一個DataURL(base64)形勢的檔案。當檔案大於指定引數時,它將通過file-loader進行打包。

配置url-loader:

const path = require('path');

const appSrc = path.resolve(__dirname, '../src')

module.exports =  {
    mode: 'development',
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    // 指定babel預處理轉義
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            // url-loader的配置
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    // 設定url-loader轉DataURL的檔案大小上限
                    limit: 10000
                }
            }
        ]
    }
}
複製程式碼

url-loader還有兩個引數mimetypefallback,這兩個引數使用的並不多,就不在這裡贅述了。

style-loader和css-loader

style-loader和css-loader都是用來處理css檔案的,不過它們的作用並不相同。

css-loader:用來讀取css檔案的內容,並進行處理 如:minimize。

style-loader:將通過import形式匯入到js中的css檔案插入到<style></style>標籤內。

在webpack中的配置如下:

const path = require('path');

const appSrc = path.resolve(__dirname, '../src')

module.exports =  {
    mode: 'development',
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    // 指定babel預處理轉義
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    // 設定url-loader轉DataURL的檔案大小上限
                    limit: 10000
                }
            },
            // 針對css檔案配置style-loader和css-loader
            {
                test: /\.css$/,
                include: appSrc,
                use: [
                        'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            // 可以包含一些配置
                            modules:true|false, // 是否開啟css模組化,開啟後引入的css檔案僅針對當前頁面有效,不會作用到全域性
                            minimize: true // 開發模式下應該設為false,優化打包速度
                        }
                    }
                ]
            }
        ]
    }
}
複製程式碼

如上所示,當我們在針對同一型別的檔案配置多個loader時。可以將loader宣告在一個陣列內,陣列項可以是一個物件,也可以僅僅是一個字串,這取決於你針對某個loader還有沒有特殊的設定。比如在配置css-loader時,還宣告瞭option選項,並在option選項內開啟了minimize選項。但是在配置style-loader時,僅僅寫了一個字串。

需要注意的是,陣列內loader的執行順序是從陣列的最後一項依次向前執行。所有我們將css-loader配置在了後面,它是先執行的。這更符合處理邏輯,先對css進行處理,再插入到html中。

外掛

外掛是webpack的一個極其重要的功能,webpack提供了豐富的外掛介面,使開發者能夠自由的開發外掛來擴充webpack的功能。

這裡我們拿大名鼎鼎的 HtmlWebpackPlugin 來舉例。

設想一個場景,在打包時,需要手動的去建立一個html檔案,然後在其中引入打包好的各種檔案。即使建立好html檔案後,由於在config中設定了hash形式的打包檔名稱。我們在每次打包後還需要根據hash名稱的變動去改變html內引入的檔名稱,這是非常低階的重複勞作。

HtmlWebpackPlugin 為我們解決了這個問題。HtmlWebpackPlugin 能夠根據我們提供的模板自動生成html檔案,並引入打包後的內容。

下面介紹一下HtmlWebpackPlugin的使用過程。

安裝:npm install --save-dev html-webpack-plugin

安裝完成後,先在專案的根目錄建立一個資料夾public,在其中建立一個模板檔案index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
複製程式碼

然後在webpack中配置外掛:

const path = require('path');

const appSrc = path.resolve(__dirname, '../src')
// 引入html-webpack-plugin外掛
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports =  {
    mode: 'development',
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    // 指定babel預處理轉義
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    // 設定url-loader轉DataURL的檔案大小上限
                    limit: 10000
                }
            },
            // 針對css檔案配置style-loader和css-loader
            {
                test: /\.css$/,
                include: appSrc,
                use: [
                        'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            // 可以包含一些配置

                            minimize: true // 開發模式下應該設為false,優化打包速度
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        // HTML模板檔案處理外掛
        new HtmlWebpackPlugin({
            file: 'index.html', // 生成的檔名稱
            template: 'public/index.html' // 指定模板檔案
        })
    ],
}
複製程式碼

現在在命令列中執行npm build,webpack將打包src目錄內的檔案。並將在根目錄生成一個build檔案,將打包的內容輸出在裡面。

這時候,我們其實已經完成了webpack的基本配置。但是現在的配置是基於development模式進行打包的,沒有進行壓縮,很顯然這並不能做為一個可釋出的版本。要修改為生產模式其實也很簡單,可以通過兩種方式去實現。

  1. 修改配置檔案中的mode選項,將development修改為production。

  2. 刪除配置中的mode選項,修改package.json scripts中的build項為 webpack --mode production --config ./config/webpack.config.js

在配置2中,使用--mode 能夠為webpack-cli設定打包模式。修改後再次打包,這時候程式碼經過webpack production模式的優化,進行了混淆壓縮,變成了釋出版本。

devServer

在日常的開發過程中,肯定不能每修改一點東西就重新build一次,這樣開發效率會受到很大的影響。這時需要啟動一個服務,來監聽檔案的變動。當檔案儲存時就重新打包,同時幫我們自動重新整理瀏覽器,方便我們及時觀察到更新。

要完成上述操作有幾種方式,這裡只介紹其中的一種,使用 webpack-dev-server 外掛。

執行 npm install --save-dev webpack-dev-server 安裝外掛,在module.explot中新增配置項 devServer

devServer的配置項有很多,這裡大概的介紹其中幾種常用的配置:

  • contentBase: '',告訴伺服器從哪個目錄中提供內容

  • https: true|false, 是否啟用https

  • compress: true|false, 是否啟用壓縮

  • host: '127.0.0.1', 指定host地址

  • port: 23333, 指定埠

  • overlay: true|false, 當出現編譯器錯誤或警告時,在瀏覽器中顯示全屏覆蓋層。

  • progress: true|false, 將執行進度輸出到控制檯。

將devServer新增到配置中:

const path = require('path');

const appSrc = path.resolve(__dirname, '../src')
// 引入html-webpack-plugin外掛
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports =  {
    // 入口
    entry: './src/index.js',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    // 指定babel預處理轉義
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    // 設定url-loader轉DataURL的檔案大小上限
                    limit: 10000
                }
            },
            // 針對css檔案配置style-loader和css-loader
            {
                test: /\.css$/,
                include: appSrc,
                use: [
                        'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            // 可以包含一些配置

                            minimize: true // 開發模式下應該設為false,優化打包速度
                        }
                    }
                ]
            }
        ]
    },
    devServer: {
        // HOST
        host: '127.0.0.1',
        // 埠
        port: 23333,
        // 報錯提示在網頁遮罩層
        overlay: true,
        // 顯示執行進度
        progress: true,
    },
    plugins: [
        // HTML模板檔案處理外掛
        new HtmlWebpackPlugin({
            file: 'index.html', // 生成的檔名稱
            template: 'public/index.html' // 指定模板檔案
        })
    ]
}
複製程式碼

需要注意的時,devServer應當用在開發環境中,所以現在需要將之前的配置進行修改。

  1. 在配置中刪除mode項。

  2. 為package.json的scripts中新增另一個啟動命令 "start": "webpack-dev-server --open --mode development --config ./config/webpack.config.js"

  3. 將之前的build項改為 webpack --mode production --config ./config/webpack.config.js

現在,執行npm build,webpack將使用production模式進行打包。執行npm start時,將使用development模式進行打包,並且webpack-dev-server將啟動一個服務,監聽檔案變更。

現在執行npm start,就可以開始進行開發了!

進階

在上面的配置中,我們已經實現了一個react專案開發環境的基本配置。但這遠遠不夠,在實際的專案中,可能會用到很多的工具來優化開發速度。同時也需要針對不同的環境寫不同的配置,做不同的優化等。並且,可能還涉及到程式碼分割、壓縮等配置。

下面,我們來一步步完善webpack的配置。

devtool

webpack中devtool選項用來控制是否生成,以及如何生成 source map。

想要了解source map,可以看一下這篇文章。簡單的說,source map就是幫助我們定位到錯誤資訊位置的檔案。正確的配置source map,能夠提高開發效率,更快的定位到錯誤位置。

webpack中devtool有很多種配置,我們可以在 這裡 瞭解它。

在開發環境中,更推薦使用cheap-module-eval-source-map,它能幫助我們準確的定位到錯誤原始碼位置的同時,也能提供更快的構建速度和構建效能。

而在生產環境中,可以不啟動任何source map(不配置devtool項),也可以使用source-map。需要注意的是,不要將source map部署到生產伺服器中。

為svg檔案配置loader

一般情況下,專案都會需要用到圖示。常見的圖示使用方式有很多種,如雪碧圖、字型圖示、svg等。雪碧圖和iconfont的使用方式不需要進行特殊的處理,這裡我們就不再贅述。下面介紹一個使用svg圖示的方法。

通過 svgr ,能夠直接將svg圖示以react元件的形式引入專案中。

就像這樣:

import React from 'react';
import { ReactComponent as Icon } from './icon.svg';

export default class App extends React.Component {
    render() {
        return <div>
            <Icon width={10} height={10} />
        </div>
    }
}
複製程式碼

在react最新版本的cli create-react-app,已近預設整合了svgr。在我們自己的專案中使用也很簡單,只需要針對 .svg新增loader即可。

{
  test: /\.svg$/,
  use: ['@svgr/webpack'],
}
複製程式碼

svgr同時也支援node、react-native等處理方式,可以通過 svgr文件來了解。

構建不同環境下的配置

在生產環境和開發環境的構建目標差異很大。比如在開發環境中,需要更快的構建速度和更強的錯誤提示。但是在生產環境中,則希望構建的程式碼能更小,更輕,更側重於效能。所以,針對不同的環境,需要不同的配置檔案。但是如果將配置完全拆分開,兩個配置檔案中可能會包含很多重複的程式碼。這時我們需要提出公共的配置,為了將這些配置合併在一起,可以使用webpack-merge

下面,我們開始使用 webpack-merge 進行配置優化。

首先,使用npm安裝依賴 npm install --save-dev webpack-merge

然後,在config資料夾下建立 webpack.config.common.js 、 webpack.config.dev.js 、webpack.config.prod.js。顧名思義,這三個配置代表了通用、開發、生產模式的配置檔案。

將之前配置中用到的公共配置提出到 webpack.config.common.js 內:

// webpack.config.common.js

// 打包HTML檔案
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const appSrc = path.resolve(__dirname, '../src')

module.exports = {
    // 入口
    entry: './src/index.js',
    module: {
        rules: [
            {
                // 配置svg圖示loader,可以在專案中通過元件的形式直接引入svg圖示
                test: /\.svg$/,
                include: appSrc,
                use: ['@svgr/webpack']
            }
        ]
    },
    plugins: [
        // HTML模板檔案處理外掛
        new HtmlWebpackPlugin({
            file: 'index.html',
            template: 'public/index.html'
        })
    ]
}

複製程式碼

開發環境下的配置:

const merge = require('webpack-merge');
// 引入公共配置檔案
const common = require('./webpack.config.common.js');
const path = require('path');

const appSrc = path.resolve(__dirname, '../src')

module.exports = merge(common, {
    mode: 'development',
    devtool: 'cheap-module-eval-source-map',
    // 出口
    output: {
        pathinfo: true,
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        // chunk名稱配置
        chunkFilename: '[name].chunk.js',
        // 輸出的檔名配置
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                // exclude: /node_modules/,
                include: appSrc,
                loader: "babel-loader",
                options: {
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            // 針對靜態檔案
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    limit: 8192,
                    name: 'static/[name].[hash:8].[ext]',
                }
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            minimize: false
                        }
                    }
                ]
            }
        ]
    },
    devServer: {
        // HOST
        host: '127.0.0.1',
        // 埠
        port: 23333,
        // 報錯提示在網頁遮罩層
        overlay: true,
        // 顯示執行進度
        progress: true,
    }
})

複製程式碼

生產環境配置檔案:

const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.config.common.js');
// 每次執行打包 先清除之前的打包檔案
const CleanWebpackPlugin = require('clean-webpack-plugin');

const appSrc = path.resolve(__dirname,'../src')

module.exports = merge(common, {
    mode: 'production',
    // 出口
    output: {
        pathinfo: false,
        chunkFilename: 'js/[name].chunk.js',
        // 所有輸出檔案的目標路徑
        // 必須是絕對路徑(使用 Node.js 的 path 模組)
        path: path.resolve(__dirname, './../build'),
        filename: "js/[name].[chunkhash:8].js"
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                include: appSrc,
                // exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: ["@babel/preset-env", "@babel/preset-react"]
                }
            },
            // 針對靜態檔案
            {
                test: /\.(png|jpg|gif)$/,
                loader: "url-loader",
                options: {
                    limit: 10000,
                    name: 'static/[name].[hash:8].[ext]',
                }
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            minimize: true
                        }
                    }
                ]
            }

        ]
    },
    plugins: [
        // 打包前清除之前的build目錄
        new CleanWebpackPlugin(['build'], path.resolve(__dirname, '../'))
    ]
});
複製程式碼

現在配置已經修改完成,我們還需要修改一下package.json,讓啟動命令去引用不同的配置檔案。

將開發模式的啟動配置修改為 "start": "webpack-dev-server --open --mode development --config ./config/webpack.config.dev.js"

生產模式的啟動配置修改為 "build": "webpack --mode production --config ./config/webpack.config.prod.js",

現在我們使用npm start命令啟動專案,執行的是webpack.config.dev.js檔案,這是開發配置檔案,我們可以在裡面做一些針對開發模式的優化。

使用npm build命令啟動專案,執行的是webpack.config.prod.js檔案,這是生產配置檔案,我們可以在裡面做一些針對生產模式的優化。

防止打包檔案的重複

執行build命令打包檔案時,會在專案的根目錄下生成build目錄,並在其中生成打包檔案。當執行多次build後,會發現由於專案名稱的hash值不同,build目錄下可能存在多個版本打包後的檔案。要解決這個問題,可以使用外掛 clean-webpack-plugin

首先安裝外掛 npm i clean-webpack-plugin --save-dev

配置如下:

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

// webpack config
{
  plugins: [
    new CleanWebpackPlugin(['build'], path.resolve(__dirname, '../'))
  ]
}
複製程式碼

配置完外掛後,再執行npm build命令。會發現每次打包前,build目錄都會被刪除,然後重新建立。

注意,該外掛只用於生產環境配置。

總結

到這裡,我們實現了webpack的基礎的配置,以及各種概念的掃盲。其實這隻能算是基礎用法,要實現一個真正完善的webpack配置肯定遠遠不止這些。這篇文章的物件導向為初學者,所有在這裡就不過多的介紹那些比較複雜的概念。

在掌握了上述基礎配置後,大家可以嘗試著進行一些更深入的學習,如optimization、tree shaking、生產環境下的構建速度優化等。

相關文章