[Webpack] 核心概念、基礎配置、常用loader和常用外掛

百五十發表於2020-04-06

Webpack 是一個JavaScript應用程式的打包模組化工具.Webpack裡一切檔案都是模組,處理應用程式時,它會遞迴構建程式中各個模組的依賴關係圖, 最後將這些模組打包成一個或者多個bundle

核心概念

入口(enrty)

入口起點指示 webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始。進入入口起點後,webpack 會找出有哪些模組和庫是入口起點(直接和間接)依賴的。

出口(output)

output 屬性告訴 webpack 在哪裡輸出它所建立的 bundles,以及如何命名這些檔案,預設值為 ./dist。基本上,整個應用程式結構,都會被編譯到你指定的輸出路徑的資料夾中。可以通過在配置中指定一個 output 欄位,來配置這些處理過程。

loader

loade 讓 webpack 能夠去處理那些非 JavaScript 檔案(webpack 自身只理解 JavaScript)。loader 可以將所有型別的檔案轉換為 webpack 能夠處理的有效模組,然後就可以利用 webpack 的打包能力,對它們進行處理。本質上,webpack loader 將所有型別的檔案,轉換為應用程式的依賴圖(和最終的 bundle)可以直接引用的模組。

外掛(plugins)

loader 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。外掛介面功能極其強大,可以用來處理各種各樣的任務。

模式

通過選擇 developmentproduction 之中的一個,來設定 mode 引數,可以啟用相應模式下的 webpack 內建的優化。

基礎配置

可以通過一個JavaScript檔案匯出Webpack的配置:

module.exports = {
  mode: "development",	// 模式
  entry: {},	// 入口
  output: {},	// 輸出
  resolve: {},	// 解析
  module: {},	// 模組
  plugins: [],	// 外掛
}
複製程式碼

mode

mode的取值有developmentproduction。通過設定mode,webpack在打包時會使用相應模式的內建優化,並process.env.NODE_ENV設定為對應的值。

entry

entry是應用的入口,可以傳入一個字串、陣列或者物件。如果傳入一個字串或字串陣列,chunk 會被命名為 main。如果傳入一個物件,則每個鍵(key)會是 chunk 的名稱,該值描述了 chunk 的入口起點。

module.exports = {
    // ...
    entry: {
        app: "./src/index.js"
    }
}
複製程式碼

output

output 可以控制 webpack 如何向硬碟寫入編譯檔案。即使可以存在多個入口起點,但只指定一個輸出配置。

  • output.path: 指定打包後內容的輸出目錄

  • output.filename: 配置每個輸出bundle的名稱,可以使用模板字串配置輸出。

    模板 描述
    [hash] 模組識別符號的hash
    [chunkhash] chunk內容的hash
    [name] 模組名稱
    [id] 模組識別符號
    [query] 模組的query
  • output.chunkFilename:配置非入口chunk檔案的名稱,預設[id].js,與filename選項一致可以使用模板字串。

  • output.publicPath:指定在瀏覽器中所引用的此輸出目錄對應的公開 URL

module.export = {
    // ...
    output: {
        path: "./dist",
        publicPath: "https://example.com",
        filename: "[name].[chunkhash].js",
        chunkFilename:  "[name].[chunkhash].js"
    }
}
複製程式碼

resolve

resolve設定模組如何被解析。

  • resolve.alias:設定匯入模組時的別名
  • resolve.modules:設定模組解析時搜尋的目錄
module.exports = {
    // ...
    resolve: {
        alias: {
            "@src": "./src/"
        },
        modules: ["./node_modules"]
    }
}
複製程式碼

module

module設定webpack如何處理不同型別的模組。

  • module.rules:配置規則陣列。在建立模組時,匹配對應的規則並應用loader處理模組。

規則(Rule)的配置:

  • rule.test:匹配模組檔案路徑
  • rule.include:匹配目錄下的檔案
  • rule.exclude:匹配不在目錄下的檔案
  • rule.use:使用loaderloader應用順序為從右到左
module.exports = {
    // ...
    rules: [{
        test: /.js$/,
        include: ['./src'],
        use: ["babel-loader"]
    }]
}
複製程式碼

plugins

plugins設定打包過程中使用的外掛。

module.exports = {
    // ...
    plugins: []
}
複製程式碼

devtool

設定source maps如何生成,開發時可以將此選項設定為cheap-module-eval-source-map

module.exports = {
    // ...
    devtool: "cheap-module-eval-source-map"
}
複製程式碼

devServer

設定webpack-dev-server使用時的選項,可以配置監聽埠、代理等。

const path = require("path")
module.exports = {
    // ...
    devServer: {
        host: "0.0.0.0",
        contentBase: path.resolve(__dirname, "build"),
		hot: true,	// 開啟模組熱替換
		historyApiFallback: true,  // 使用index.html頁面來代替404響應,使用HTML5 History API時可將該選項設定為true,例如react-router
		proxy: {
            '/api': 'http://127.0.0.1:3000'
        }
    }
}
複製程式碼

常用loader

CSS相關

用於解析CSSloader一般有:style-loadercss-loaderpostcss-loaderless-loadersass-loader

  • style-loader:通過注入<style>標籤將CSS新增到DOM
  • css-loader:解釋@importurl(),會 import/require() 後再解析它們。css-loader前應用了loader需要指定importLoaders選項
  • postcss-loader:應用postcss
  • less-loader:解析less
  • sass-loader:解析sass
module.exports = {
    // ...
    module: {
        rules: [{
            test: /.less$/,
            include: ["./src"],
            use: [
                "style-loader",
                {
                    loader: "css-loader",
                    options: {
                        importLoaders: 2
                    }
                },
                "postcss-loader",
                "less-loader"
            ]
        }]
    }
}
複製程式碼

JavaScript相關

  • babel-loaderbabel-loader基於babel,用於解析JavaScript檔案。babel有豐富的預設和外掛,babel的配置可以直接寫到options裡或者單獨寫道配置檔案裡。

幾個babel常用的預設和外掛:

  • @babel/preset-env:用於轉換最新的JavaScript程式碼、為低版本瀏覽器提供polyfill
  • @babel/preset-react:用於解析jsx內容,該預設包含了@babel/plugin-syntax-jsx@babel/plugin-transform-react-jsx@babel/plugin-transform-react-display-name三個外掛。當指定了development選項時,還額外包含@babel/plugin-transform-react-jsx-self@babel/plugin-transform-react-jsx-source兩個外掛
  • @babel/preset-typescript:用於解析typescript內容,該預設僅包含@babel/plugin-transform-typescript外掛。在解析typescript內容時,該預設會直接把typescript相關的內容去掉
  • @babel/plugin-proposal-class-properties:用於解析類屬性的外掛
  • @babel/plugin-proposal-object-rest-spread:用於解析析構物件的外掛
  • @babel/plugin-transform-runtime:用於複用babel內建的helper程式碼從而減小程式碼體積

babel常用的預設有preset-envpreset-reactpreset

module.export = {
    // ...
    module: {
        rules: [{
            test: /.js$/,
            include: ["./src/"],
            use: [{
                loader: "babel-loader",
                options: {
                    presets: [
                       "@babel/preset-env",
                        "@babel/preset-react",
                        "@babel/preset-typescript",
                    ],
                    plugins: [
                        "@babel/plugin-proposal-class-properties",
   						 "@babel/plugin-proposal-object-rest-spread",
                    ]
                }
            }]
        }]
    }
}
複製程式碼

其他資源

除了CSSJavaScript資源外,專案裡還有其他的資源,例如圖片、字型等。對於這類資源,可以通過file-loaderurl-loader解析。

  • file-loader:告訴Webpack引用的模組是一個檔案,並返回其打包後的url
  • url-loader:作用與file-loader類似,但當檔案大小(單位 byte)低於指定的限制時,可以返回一個 DataURL。
module.exports = {
    // ...
    module: {
        rules: [{
        	test: /\.(png|jpg|gif)$/,
        	use: [{
            	loader: 'url-loader',
            	options: {
              		limit: 8192
            	}
          	}]
        }]
    }
}
複製程式碼

常用外掛

copy-webpack-plugin

copy-webpack-plugin用於打包時複製檔案。

const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
    // ...
    plugins: [
        new CopyWebpackPlugin({
        	{
        		from: "./public",
        		to: "./build/"
      		}
        })
    ]
} 
複製程式碼

html-webpack-plugin

生成一個 HTML5 檔案,包括使用 script 標籤的 body 中的所有 webpack 包。

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
	// ...
	plugins: [
        new HtmlWebpackPlugin({
            template: "./public/index.html",
      		chunks: ["app"]
        })
    ]
};
複製程式碼

add-asset-html-webpack-plugin

插入一個JavaScript或者CSS資源到html-webpack-plugin生產的html頁面中。使用DLL時,可以通過該外掛將生成的JavaScript檔案插入到頁面中。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const path = require("path")
module.exports = {
	// ...
	plugins: [
		new HtmlWebpackPlugin(),
		new AddAssetHtmlPlugin({
            filepath: path.resolve(__dirname, './dlls/*.dll.js'),
      		outputPath: "dlls",
      		publicPath: "/dlls"
        })
    ]
};
複製程式碼

clean-webpack-plugin

clean-webpack-plugin可以清理檔案,可以用於打包時清理之前打包的檔案。

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
	// ...
	plugins: [
		new CleanWebpackPlugin()
    ]
};
複製程式碼

mini-css-extract-plugin

將CSS提取到單獨的檔案中,為每個包含CSS的JS檔案建立一個CSS檔案,同時該外掛支援CSS和SourceMap的按需載入。使用該外掛時,style-loader需要替換為該外掛提供的loader

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
	// ...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,	// 需要替換 style-loader
					{
						loader: "css-loader",
						options: {
							importLoaders: 1
						}
					}
				]
            }
        ]
    },
	plugins: [
		new MiniCssExtractPlugin({
			filename: "static/css/[name].[chunkhash:8].css",
			chunkFilename: "static/css/[name].[chunkhash:8].chunk.css"
		})
    ]
};
複製程式碼

webpack.HotModuleReplacementPlugin

模組熱替換外掛,開啟HMR。可以和webpack.NamedModulesPlugin搭配使用,顯示模組的相對路徑,建議用於開發環境。

const webpack = require('webpack');
module.exports = {
	// ...
	plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin(),
    ]
};
複製程式碼

webpack.DefinePlugin

建立一個在編譯時可以配置的全域性常量。

const webpack = require('webpack');
module.exports = {
	// ...
	plugins: [
        new webpack.DefinePlugin({
            VERSION: JSON.stringify("0.0.1")
        })
    ]
};
複製程式碼

因為這個外掛直接執行文字替換,給定的值必須包含字串本身內的實際引號。通常,有兩種方式來達到這個效果,使用'"production"', 或者使用 JSON.stringify('production')

webpack.DllPluginwebpack.DllReferencePlugin

DllPlugin和DllReferencePlugin提供了一種拆分包的方法,可以極大地提高構建時間效能。

// 製作dll
const webpack = require("webpack");
const path = require("path");

module.exports = {
	mode: "production",
	entry: {
        react: ["react", "react-dom"],
        antd: ["antd"],
        utils: ["lodash", "moment"]
    },
    output: {
		path: "./dlls",
		filename: "[name].dll.js",
		library: "[name]_[hash]"
	},
	resolve: {
		extensions: [".js", ".jsx"],
		modules: ["./node_modules"]
	},
	plugins: [
		new webpack.DllPlugin({
			path: path.resolve(__dirname, "./dlls/[name].manifest.json"),
			name: "[name]_[hash]"
		})
	]
};
複製程式碼
// 使用dll
const webpack = require("webpack");
const path = require("path");

module.exports = {
	// ...
	plugins: [
		new webpack.DllReferencePlugin({
			manifest: path.resolve(__dirname, "./dlls/react.manifest.json"),
		}),
        new webpack.DllReferencePlugin({
			manifest: path.resolve(__dirname, "./dlls/antd.manifest.json"),
		}),
        new webpack.DllReferencePlugin({
			manifest: path.resolve(__dirname, "./dlls/utils.manifest.json"),
		}),
		new AddAssetHtmlWebpackPlugin({
			filepath: path.resolves(__dirname,  './dlls/*.dll.js'),
			outputPath: "dlls",
			publicPath: "/dlls"
		})
	]
};
複製程式碼

相關文章