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 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。外掛介面功能極其強大,可以用來處理各種各樣的任務。
模式
通過選擇 development
或 production
之中的一個,來設定 mode
引數,可以啟用相應模式下的 webpack 內建的優化。
基礎配置
可以通過一個JavaScript檔案匯出Webpack的配置:
module.exports = {
mode: "development", // 模式
entry: {}, // 入口
output: {}, // 輸出
resolve: {}, // 解析
module: {}, // 模組
plugins: [], // 外掛
}
複製程式碼
mode
mode
的取值有development
和production
。通過設定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
:使用loader
,loader
應用順序為從右到左
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相關
用於解析CSS
的loader
一般有:style-loader
、css-loader
、postcss-loader
、less-loader
和sass-loader
。
style-loader
:通過注入<style>
標籤將CSS新增到DOMcss-loader
:解釋@import
和url()
,會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-loader
:babel-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-env
、preset-react
、preset
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",
]
}
}]
}]
}
}
複製程式碼
其他資源
除了CSS
和JavaScript
資源外,專案裡還有其他的資源,例如圖片、字型等。對於這類資源,可以通過file-loader
或url-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.DllPlugin
和 webpack.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"
})
]
};
複製程式碼