前言
webpack的配置真的是又多又杂,而且随着webpack从v4到v5之后又发生很多的变化,正好把一个react+ts项目中的webpack5的配置全部梳理了一遍,在此记录一下实践过程,以便以后查阅起来更方便。
配置文件目录结构
首先是配置文件的目录结构。
.
├── utils.js
├── webpack.common.js // 公有webpack配置文件
├── webpack.dev.js // development环境配置文件
└── webpack.prod.js // production环境配置文件
webpack依赖安装
npm i webpack webpack-cli -D
配置输入输出
- 项目入口文件配置:
// webpack.common.js
module.exports = {
entry: {
index: './src/index.tsx'
}
}
- 项目输出文件配置: development模式的output如下:
output: {
publicPath: '/',
filename: 'js/[name].bundle.[fullhash].js',
assetModuleFilename: 'img/[hash][ext][query]',
path: resolveApp('dist'),
}
production模式的output如下:
output: {
filename: 'js/[name].[contenthash:8].bundle.js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
assetModuleFilename: 'img/[hash][ext][query]',
publicPath: '',
path: resolveApp('dist'),
}
- 文件名中的hash的作用
打包文件中的hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。
- 输出文件的fullhash, chunkhash, contenthash区别
- fullhash: 若打包文件有任何一个文件发生了改动,hash值都会发生变化。
- chunkhash: chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值,这样可以避免所有打包的hash值都改变。
- contenthash: contenthash主要根据提取出来的文件的内容进行hash。
我们的项目开发环境采用的js的输出文件名是fullhash(hash格式webpack5已被废弃)输出格式,生产环境采用的是contenthash的输出格式。
- 为什么不使用chunkhash而使用contenthash? 可以参考此文章:webpack的hash、chunkhash、contenthash
loader配置
ts,tsx相关文件支持
- 依赖安装:
npm i esbuild-loader babel-loader ts-loader -D
- 配置编写
// webpack.common.js
const devMode = process.env.NODE_ENV !== "production";
module: {
rules: [{
test: /\.(jsx|tsx)$/,
include: resolveApp('src'),
use: devMode ? [
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
tsconfigRaw: require('../tsconfig.json')
},
}
] : ['babel-loader', {
loader: 'ts-loader',
options: {
transpileOnly: devMode,
}
}
]
}]
}
为了考虑编译速度,我们在本地开发环境采用编译速度更快esbuild-loader, 而开发环境我们需要更多的考虑兼容和稳定性,还是用babel-loader和ts-loader的组合。
css, scss文件支持
- 依赖安装
npm i style-loader css-loader postcss-loader sass-loader mini-css-extract-plugin -D
// webpack.common.js
{
test: /.s?css$/,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 2,
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
],
],
},
},
},
"sass-loader",
]
},
图片资源支持
// webpack.common.js
{
test: /\.(png|svg|jp[e]g|gif)$/i,
include: [
resolveApp('src')
],
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8kb
}
},
type: 'asset',
}
在webpack5之前可能需要各种loader处理静态资源,webpack5后资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。
字体文件支持
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
include:[
resolveApp('src')
],
type: 'asset/resource'
}
webpack-dev-server配置
由于webpack-dev-server是在development环境下的配置项,所以配置项写在webpack.dev.js文件之中,整个配置如下。
// webpack.dev.js
const { merge } = require('webpack-merge')
const { resolveApp } = require('./utils')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
output: {
publicPath: '/',
filename: 'js/[name].bundle.[fullhash].js',
assetModuleFilename: 'img/[hash][ext][query]',
path: resolveApp('dist'),
},
devServer: {
static: {
directory: resolveApp('dist'),
},
compress: true,
port: 3000,
hot: true,
host: 'local.m.jd.com'
},
})
为了实现较快的启动速度,采取的devtool的模式是'eval-cheap-module-source-map'
;
而插件@pmmmwh/react-refresh-webpack-plugin
的作用是实现react代码可以热更新。以上就是一个简单的development环境的配置。
plugins配置
webpack.common.js的配置如下:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
title: '测试页面',
template: 'public/index.html'
}),
new ProgressBarPlugin({
format: `:msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`
})
]
}
html-webpack-plugin
:该插件将为你生成一个 HTML5 文件, 在 body 中使用script
标签引入你所有 webpack 生成的 bundle。progress-bar-webpack-plugin
:该插件可以在命令行展示webpack编译进度。
webpack.dev.js的配置如下:
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
...
plugins: [
new ReactRefreshWebpackPlugin()
]
}
@pmmmwh/react-refresh-webpack-plugin
:用于为 React 组件启用“快速刷新”。
webpack.prod.js的配置如下:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CmpWebpackPlugin = require("cmp-webpack-plugin")
module.exports = {
...
plugins: [
new BundleAnalyzerPlugin(),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "css/index.[contenthash:8].css",
chunkFilename: 'css/index.[contenthash:8].chunk.css'
}),
new CmpWebpackPlugin({
format: "zip",
output: resolveApp('./dist/webpack5'),
src: resolveApp('./dist'),
test: ["js/**", "*.html", "css/**", "img/**"],
name: 'webpack5'
})
]
}
webpack-bundle-analyzer
:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。clean-webpack-plugin
:一个用于清理文件的插件,在production环境可以清除之前的打包文件。mini-css-extract-plugin
:本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。cmp-webpack-plugin
:一个提供压缩功能的插件,这里是将dist目录下所有资源文件打成一个zip包。
优化配置
webpack.common.js配置如下:
module.exports = {
...
resolve: {
modules: ["src", "node_modules"],
extensions: ['.js', '.jsx', '.ts', '.tsx'], // 因为我的项目只有这两种类型的文件,如果有其他类型,需要添加进去。
},
cache: {
type: 'filesystem', // 使用文件缓存
}
}
webpack.prod.js配置如下:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
...
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 20000,
cacheGroups: {
default: {
idHint: "",
reuseExistingChunk: true,
minChunks: 2,
priority: -20
},
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
minChunks: 1,
maxSize: 20000,
priority: -10
}
},
},
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
extractComments: true,
terserOptions: {
compress: {
drop_console: false,
drop_debugger: false
}
}
}),
new CssMinimizerPlugin()
]
}
}
关于webpack的production环境的优化可以点此查看。