DefinePlugin
定义全局变量。
const webpack = require('webpack')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
// 值要求的是一个代码片段
API_BASE_URL: JSON.stringify('https://api.example.com')
})
]
}
Tree Shaking
production 模式会自动启用。其他模式需要手动开启。
module.exports = {
mode: 'none',
optimization: {
usedExports: true, // 标记未使用对象
minimize: true, // 压缩代码并去掉未引用对象
}
}
Tree Shaking 与 Babel
新版本的 babel-loader 并不会导致 Tree Shaking 失效。Tree Shaking 能使用的前提是使用 ESM ,如果 babel-loader 开启了 ESM 转换到 CommonJS,Tree Shaking 就会失效。
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env', { modules: false }]] // 不转换,Tree Shaking 生效
// presets: ['@babel/preset-env'] // 不转换,Tree Shaking 生效
// presets: [['@babel/preset-env', { modules: 'commonjs' }]] // 转换,Tree Shaking 失效
}
}
}
]
},
optimization: {
usedExports: true,
// concatenateModules: true,
minimize: true,
}
合并模块
concatenateModules 属性会尽量把多个模块合并成一个函数。
module.exports = {
mode: 'none',
optimization: {
usedExports: true, // 标记未使用对象
concatenateModules: true,
minimize: true, // 压缩代码并去掉未引用对象
}
}
SideEffects
Webpack 4 开始支持的特性,一般用于标记 npm 包是否有副作用。
optimization: {
sideEffects: true, // 开启 sideEffects
}
package.json 中 可以标记是否有副作用。sideEffects 设置为 false 表示没有副作用。
"sideEffects": false
或者设置成一个数组,存放有副作用的文件的路径。
"sideEffects": [
"./src/extend.js",
"*.css"
]
代码分割
如果是一个大型项目,打包到一个文件,可能导致打包过后的文件体积很大,大多数情况下并不需要把全部模块一并加载,所以可以实现代码的分包/分割。
多入口打包
一般用于多页应用。
示例项目结构:
配置文件:
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'none',
entry: {
index: './src/index.js',
album: './src/album.js'
}, // 指定打包的文件
output: {
filename: '[name].bundle.js' // [name] 可以自动替换成 entry 的名字
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/index.html',
filename: 'index.html',
chunks: ['index'] // 指定入口文件,如果不配置,那么 html 文件 会把所有打包结果加载进来
}),
new HtmlWebpackPlugin({
title: 'Multi Entry',
template: './src/album.html',
filename: 'album.html',
chunks: ['album']
})
]
}
打包结果:
提取公共模块
在多个模块中,可能会导入相同的 CSS 或者 JS 模块,这样打包时就有重复的部分了,应该提取出来,整合到一个打包结果。要实现这个功能,配置 optimization 中的 splitChunks 属性即可。
// webpack.config.js
optimization: {
splitChunks: {
// 自动提取所有公共模块到单独 bundle
chunks: 'all'
}
},
动态导入
动态导入可以实现按需加载,动态导入的模块会自动被分包。
示例项目结构:
index.js:
// import posts from './posts/posts'
// import album from './album/album'
const render = () => {
const hash = window.location.hash || '#posts'
const mainElement = document.querySelector('.main')
mainElement.innerHTML = ''
// 动态导入核心代码
if (hash === '#posts') {
// mainElement.appendChild(posts())
import('./posts/posts').then(({ default: posts }) => {
mainElement.appendChild(posts())
})
} else if (hash === '#album') {
// mainElement.appendChild(album())
import('./album/album').then(({ default: album }) => {
mainElement.appendChild(album())
})
}
}
render()
window.addEventListener('hashchange', render)
打包结果:
魔法注释
上述动态导入的打包结果,默认名称是序号,如果需要更改,可以使用魔法注释。webpackChunkName 设置成一样,那么会合并到一个文件。
// 动态导入核心代码
if (hash === '#posts') {
// mainElement.appendChild(posts())
import(/* webpackChunkName: 'posts' */'./posts/posts').then(({ default: posts }) => {
mainElement.appendChild(posts())
})
} else if (hash === '#album') {
// mainElement.appendChild(album())
import(/* webpackChunkName: 'album' */'./album/album').then(({ default: album }) => {
mainElement.appendChild(album())
})
}
打包结果:
CSS 按需加载
mini-css-extract-plugin 可以提取 CSS 部分,从而实现按需加载。
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
mode: 'none',
entry: {
main: './src/index.js'
},
output: {
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Dynamic import',
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin()
]
}
打包结果:
压缩打包后的 CSS 文件
optimize-css-assets-webpack-plugin 插件可以将 mini-css-extract-plugin 提取出来的 CSS 文件进一步压缩。这种压缩类的插件,一般不配置在 plugins 数组中,而是 optimization 中的 minimizer 数组。minimizer 在生产模式下会自动开启。但是如果配置了 minimizer 数组,那么 Webpack 会认为开发者要使用自定义的压缩插件,这个时候需要再手动配置压缩 JS 的插件 terser-webpack-plugin。
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin(),
new TerserWebpackPlugin(),
]
},
输出文件名 Hash
如果打包结果文件以特定规则命名,那么部署后会导致缓存的问题,所以把文件名设置为文件的 hash 值不失为一种好办法。Webpack 支持3种哈希: hash、chunkhash、contenthash。
- hash
项目级别的哈希,只要项目中任意文件的内容发生改变,整个项目的哈希都会改变。且所有文件都会使用相同的哈希。
- chunkhash
同一路的打包使用同一个哈希。
- contenthash
文件级别的哈希,当文件改变,只会改变相关的文件。contenthash 后面可以指定哈希长度。
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'none',
entry: {
main: './src/index.js'
},
output: {
filename: '[name]-[contenthash:8].bundle.js'
},
optimization: {
minimizer: [
new TerserWebpackPlugin(),
new OptimizeCssAssetsWebpackPlugin()
]
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 'style-loader', // 将样式通过 style 标签注入
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Dynamic import',
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name]-[contenthash:8].bundle.css'
})
]
}