const path = require('path')
const EslintWebpackPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
//封装处理样式配置
const getStyleloaders = (pre)=>{
return [
'style-loader',
'css-loader',
// 处理 css 兼容性问题
//需要配合 package.json 中的browserslist 来指定兼容性做到什么程度
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: 'postcss-preset-env'
}
}
},
pre
].filter(Boolean)
};
//要用 commonjs的方式以对象的模式暴露出去
module.exports = {
// 入口文件
entry: './src/main.js',
// 出口文件
output: {
path: undefined,
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[hash:10][etx][query]'
},
// 配置loader
module: {
rules: [
// 处理 css
{
test: /\.css$/,
use: getStyleloaders()
},
{
test: /\.less$/,
use: getStyleloaders('less-loader')
},
{
test: /\.s[ac]ss$/,
use: getStyleloaders('sass-loader')
},
{
test: /\.styl$/,
use: getStyleloaders('stylus-loader')
},
// 处理 图片
{
test:/\.(jpe?g|png|gif|webp|svg)$/,
type:'asset',
parser:{
dataUrlCondition:{
maxSize:10*1024,
}
}
},
//处理其他资源
{
test:/\.(woff2?|ttf)$/,
type:'asset/resource'
},
// 处理 js
{
test:/\.jsx?$/,
include:path.resolve(__dirname,'../src'),
loader:'babel-loader',//babel 也需要有一个 babel 的配置文件
options:{
catchDirectory:true,
catchCompression:false,
plugins:[
'react-refresh/babel',//用来解决react脚手架下 js不能热更新的问题
]
}
}
]
},
// 配置plugin
// 处理html
plugin:[
//eslint 插件也是需要有自己的配置文件
new EslintWebpackPlugin({
context:path.resolve(__dirname,'../src'),
exclude:'node_modules',
cache:true,
cacheLocation:path.resolve(__dirname,'../node_modules/.catch/.eslintcache'),
}),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html'),
}),
new ReactRefreshWebpackPlugin()//用来解决react脚手架下 js不能热更新的问题
],
// 环境设置
mode:'development',
devtool:'cheap-module-source-map',
optimization:{
splitChunks:{
chunks:'all'
},
runtimeChunk:{
name:(entrypoint)=>`runtime~${entrypoint.name}.js`,
}
},
//webpack 解析模块加载选项
// 解决运行react项目时报错 Module not found:Error:can't resolve './App' in 'xx/xxx/xxx/x'
resolve:{
//自动补全文件扩展名
extentions:['.jsx','.js','json']
},
devServer:{
host:'localhost',
port:3000,
open:true,
hot:true,
historyApiFallback:true,//解决前端路由刷新404问题
}
}
const path = require('path')
const EslintWebpackPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 生产模式下不需要激活热更新
// const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
// CSS提取为单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// css 压缩文件
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
// js 压缩,使用css压缩后必须使用的插件
const TerserWebpackPlugin = require('terser-webpack-plugin')
// 图片 压缩插件
const ImageMinimizerWebpackPlugin = require('image-minimizer-webpack-plugin')
// 如果出现 网站图标 需要用该复制插件将其复制到dist目录下
const CopyPlugin = require("copy-webpack-plugin");
const getStyleloaders = (pre) => {
return [
// 'style-loader',
MiniCssExtractPlugin.loader,//将style-loader替换为插件loader,打包为单独文件
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: 'postcss-preset-env'
}
}
},
pre
].filter(Boolean)
};
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, '../dist'),//设置出口文件路径
filename: 'static/js/[name].[contenthash:10].js',//加入哈希值
chunkFilename: 'static/js/[name].[contenthash:10].chunk.js',//加入哈希值
assetModuleFilename: 'static/media/[hash:10][etx][query]',
clean: true,//将上次打包内容清空
},
module: {
rules: [
{
test: /\.css$/,
use: getStyleloaders()
},
{
test: /\.less$/,
use: getStyleloaders('less-loader')
},
{
test: /\.s[ac]ss$/,
use: getStyleloaders('sass-loader')
},
{
test: /\.styl$/,
use: getStyleloaders('stylus-loader')
},
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
}
},
{
test: /\.(woff2?|ttf)$/,
type: 'asset/resource'
},
{
test: /\.jsx?$/,
include: path.resolve(__dirname, '../src'),
loader: 'babel-loader',
options: {
catchDirectory: true,
catchCompression: false,
// 生产模式下不需要激活热更新
// plugins:[
// 'react-refresh/babel',
// ]
}
}
]
},
plugin: [
new EslintWebpackPlugin({
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules',
cache: true,
cacheLocation: path.resolve(__dirname, '../node_modules/.catch/.eslintcache'),
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
}),
// new ReactRefreshWebpackPlugin(),// 生产模式下不需要激活热更新
// css 打包为单独文件插件
new MiniCssExtractPlugin({
filename: 'static/css/name.[contenthash:10].css',
chunkFilename: 'static/css/name.[contenthash:10].chunk.css'
}),
// 复制插件 处理public下其他资源 如网站图标
new CopyPlugin({
patterns: [
// 设置从何处复制到何处
{
from: path.resolve(__dirname,'../public'),
to: path.resolve(__dirname,'../dist')
},
],
globOptions: {
//设置忽略 index.html 文件
ignore: ['**/index.html']
}
}),
],
mode: 'production', // 改为生产模式
devtool: 'source-map',//使用 source-map
optimization: {
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`,
},
// 压缩插件放在 optimization 对象内
minimizer: [
new CssMinimizerWebpackPlugin(),//压缩 css 插件,
new TerserWebpackPlugin(),//压缩 js 插件,
//压缩图片插件,
new ImageMinimizerWebpackPlugin({
minimizerOptions: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: extendDefaultPlugins([
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
]),
},
],
],
},
}),
]
},
resolve: {
extentions: ['.jsx', '.js', 'json']
},
//生产模式不需要devsever
// devServer:{
// host:'localhost',
// port:3000,
// open:true,
// hot:true,
// historyApiFallback:true,
// }
}
将生产模式和开发模式的配置进行合并,通过 process.env.NODE_ENV 的值来判断当前为生产环境还是开发环境。
const isProd = process.env.NODE_ENV ==='production'
//三元判断 加载哪一个插件
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
//根据是否为生产模式来决定是否加载插件
plugins:[
!isProd && 'react-refresh/babel',//用来解决react脚手架下 js不能热更新的问题
].filter(Boolean)