Use Webpack5 to build a project (react)

This article undertakes  to use Webpack5 to build the project 

 

Development mode :

Configure webpack.dev.js

Create a new project, generate a package.json file, create a config folder, and create a webpack.dev.js file

Configure webpack.dev.js

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
//返回处理样式的loader函数
getStyleLoaders=function(pre){
    return[
        "style-loader", "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
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][ext][query]"
    },
    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:{
                    dataCondition:{
                        maxSize:10*1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test:/\.(woff2?|ttf)/,
                type:"asset/resource",
            },
            //处理js

        ]
    },
    plugins:[
        new EslintWebpackPlugin({
            context:path.resolve(__dirname,'../src'),
            exclude:'node_modules',
            cache:true,
            cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache')
        })
    ]
}

Deploy package.json,browserslist

{
  "name": "webpack-react",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "browserslist":[
    "last 2 version",
    "> 1%",
    "not dead"
  ]
}

Create and configure .eslintrc.js

module.exports={
    extends:["react-app"], //继承官方react规则
    parserOptions:{
        babelOptions:{
            presets:[
                //解决页面报错的问题
                ["babel-preset-react-app",false],
                "babel-preset-react-app/prod"
            ]
        }
    }
}

Continue to configure and process js files

//处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false
                }
            }

Configure the babel.config.js file at the same time

module.exports={
    presets:["react-app"] //内置了corejs,runtime插件,具体详情可以去github上查看。
}

Add HtmlWebpackPlugin, configuration mode, and devtool:

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
//返回处理样式的loader函数
getStyleLoaders=function(pre){
    return[
        "style-loader", "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
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][ext][query]"
    },
    module: {
        ...
    plugins:[
        ...
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,"../public/index.html")
        })
    ],
    mode:"development",
    devtool:"cheap-module-source-map"
}

Configure code splitting:

optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        }
    }

Configure devServer:

devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true
    }

This is the current basic configuration:

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
//返回处理样式的loader函数
getStyleLoaders=function(pre){
    return[
        "style-loader", "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
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][ext][query]"
    },
    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:{
                    dataCondition:{
                        maxSize:10*1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test:/\.(woff2?|ttf)$/,
                type:"asset/resource",
            },
            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false
                }
            }
        ]
    },
    plugins:[
        new EslintWebpackPlugin({
            context:path.resolve(__dirname,'../src'),
            exclude:'node_modules',
            cache:true,
            cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,"../public/index.html")
        })
    ],
    mode:"development",
    devtool:"cheap-module-source-map",
    optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        }
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true
    }
}

Create and configure the main.js file:

import React from 'react' 
import ReactDom from "react-dom/client"
import App from "./App"

const root = ReactDom.createRoot(document.getElementById("app"))
root.render(<App/>)

Create and configure App.jsx

import React from "react";

function App(){
    return <h1>App</h1>
}

export default App

Create and configure the public/index.html file

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React Cli</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

Start downloading dependencies:

npm i eslint-webpack-plugin html-webpack-plugin style-loader css-loader postcss-loader postcss-preset-env less-loader sass-loader sass stylus-loader -D

npm i babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D

npm i webpack-dev-server webpack webpack-cli -D

npm i react react-dom

 After the download is complete, configure package.jason

"scripts": {
    "start":"npm run dev",
    "dev":"webpack serve --config ./config/webpack.dev.js"
  },

Enter the command: npm start, and the page reports an error:

 Here we need babel-preset-app to ask us to make a NODE_ENV or BABEL_ENV environment variable, we download the cross-env library to set the environment variable:

npm install --save-dev cross-env

Modify package.json:

"scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  },

Enter npm start and continue to report errors:

When we introduced it, we didn’t have the function of auto-completion for js files, but here are jsx files, so continue to configure webpack.dev.js 

optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        }
    },
    //webpack解析块加载选项
    resolve:{
        //自动补全文件拓展名
        extensions:[".jsx",".js",".json"]
    },

Compile again and there is no problem.

Detect HMR hot update, the style file can be triggered, but the js file still cannot, so refer to the plugin, react-refresh-webpack-plugin

npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh

Configure webpack.dev.js

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
...
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][ext][query]"
    },
    module: {
        rules: [
            ...
            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false,
                    plugins:["react-refresh/babel"]//激活js文件HMR功能
                }
            }
        ]
    },
    plugins:[
        ...
        new ReactRefreshWebpackPlugin()
    ],
    ...
}

 Next is the routing setup: App.jsx

import React from "react";
import Home from "../src/pages/Home/Home"
import {Link, Routes,Route} from "react-router-dom"
import About from './pages/About/About'

function App() {
    return (<div>
                <h1>App</h1>
                <ul>
                    <li><Link to="/home">Home</Link></li>
                    <li><Link to="/about">About</Link></li>
                </ul>
                <Routes>
                    <Route path="/home" element={<Home/>}/>
                    <Route path="/about" element={<About/>}/>
                </Routes>
            </div>)
}

export default App
import React from 'react'

export default function About() {
  return (
    <div>About</div>
  )
}
import React from 'react'
import "./Home.less"
export default function Home() {
  return (
    <div className='home'>Home</div>
  )
}

The page can be restarted, but there will be a 404 problem when refreshing the page. Solution: historyApiFallback, configure webapck.dev.js

devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true//解决前端路由刷新404的问题
    }

If you want routes to be packaged separately, you need to configure lazy loading of routes:

import React, { Suspense, lazy } from "react";
// import Home from "../src/pages/Home/Home"
import { Link, Routes, Route } from "react-router-dom"
// import About from './pages/About/About'
const Home = lazy(() => import("./pages/Home/Home.jsx"))
const About = lazy(() => import("./pages/About/About.jsx"))
function App() {
    return (<div>
        <h1>App</h1>
        <ul>
            <li><Link to="/home">Home</Link></li>
            <li><Link to="/about">About</Link></li>
        </ul>
        <Suspense fallback={<div>loading...</div>}>
            <Routes>
                <Route path="/home" element={<Home />} />
                <Route path="/about" element={<About />} />
            </Routes>
        </Suspense>
    </div>)
}

export default App

At the same time, you can name the specified magic, which can be packaged into the name you want:

const Home = lazy(() => import(/* webpackChunkName:'home' */"./pages/Home/Home.jsx"))
const About = lazy(() => import(/* webpackChunkName:'about' */"./pages/About/About.jsx"))

Regarding the configuration of favicon.ico, put the file under the public folder, then configure index.html, and import the link:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
    <title>React Cli</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

 Production mode:

We directly copy a copy of dev, modify the name to webpack.prod.js, and modify the configuration:

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][ext][query]",
        clean:true
    },

 Process css, package files separately and compress css

...
const  MiniCssExtractPlugin= require("mini-css-extract-plugin")
const  CssMinimizerWebpackPlugin= require("css-minimizer-webpack-plugin")
//返回处理样式的loader函数
let getStyleLoaders=function(pre){
    return[
        MiniCssExtractPlugin.loader, "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
module.exports = {
   ...
    module: {
        rules: [
            ...
            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false,
                    plugins:["react-refresh/babel"]//激活js文件HMR功能
                }
            }
        ]
    },
    plugins:[
        ...
        new MiniCssExtractPlugin({
            filename:'static/css/[name].[contenthash:10].css',
            chunkFilename:'static/css/[name].[contenthash:10].chunk.css'
        })
    ],
    mode:"development",
    devtool:"cheap-module-source-map",
    optimization:{
        ...
        minimizer:[
            new CssMinimizerWebpackPlugin()
        ]
    },
    ...
}

Handle compressed js

const  TerserWebpackPlugin= require("terser-webpack-plugin")
...

optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        },
        minimizer:[
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin()
        ]
    },

Change mode to production, change devtool to: source-map, delete devServer, delete HMR function,

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const  MiniCssExtractPlugin= require("mini-css-extract-plugin")
const  CssMinimizerWebpackPlugin= require("css-minimizer-webpack-plugin")
const  TerserWebpackPlugin= require("terser-webpack-plugin")
//返回处理样式的loader函数
let getStyleLoaders=function(pre){
    return[
        MiniCssExtractPlugin.loader, "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            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][ext][query]",
        clean:true
    },
    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:{
                    dataCondition:{
                        maxSize:10*1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test:/\.(woff2?|ttf)$/,
                type:"asset/resource",
            },
            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false,
                }
            }
        ]
    },
    plugins:[
        new EslintWebpackPlugin({
            context:path.resolve(__dirname,'../src'),
            exclude:'node_modules',
            cache:true,
            cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,"../public/index.html"),
        }),
        new MiniCssExtractPlugin({
            filename:'static/css/[name].[contenthash:10].css',
            chunkFilename:'static/css/[name].[contenthash:10].chunk.css'
        })
    ],
    mode:"production",
    devtool:"cheap-module-source-map",
    optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        },
        minimizer:[
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin()
        ]
    },
    //webpack解析块加载选项
    resolve:{
        //自动补全文件拓展名
        extensions:[".jsx",".js",".json"]
    },
}

Compress Pictures:

const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin")




minimizer: [
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin(),
            new ImageMinimizerWebpackPlugin({
                minimizer: {
                    implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ]

Download dependencies:

npm i mini-css-extract-plugin css-minimizer-webpack-plugin -D
npm install image-minimizer-webpack-plugin imagemin --save-dev

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev

Modify the package.json package startup instruction:

"scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js",
    "build": "cross-env NODE_ENV=production webpack serve --config ./config/webpack.prod.js"
  },

Just start packaging npm run build. But found that favicon is not packaged under dist, use the plugin copy-webpack-plugin

npm i copy-webpack-plugin --save-dev
plugins: [
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:10].css',
            chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
        }),
        new CopyWebpackPlugin({//复制到dist文件下
            patterns: [
                {
                    from: path.resolve(__dirname, "../public"), to: "../dist", globOptions: {
                        ignore: ["**/index.html", "**/ignored-directory/**"],//忽略Index.html文件
                    },
                }
            ]
        })
    ],

Just after repacking.


After configuring the production and development, since there are many places where the code is repeated, let's integrate it now. The current file code is as follows:

webpack.dev.js

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
//返回处理样式的loader函数
let getStyleLoaders=function(pre){
    return[
        "style-loader", "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
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][ext][query]"
    },
    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:{
                    dataCondition:{
                        maxSize:10*1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test:/\.(woff2?|ttf)$/,
                type:"asset/resource",
            },
            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:"babel-loader",
                options:{
                    cacheDirectory:true,
                    cacheCompression:false,
                    plugins:["react-refresh/babel"]//激活js文件HMR功能
                }
            }
        ]
    },
    plugins:[
        new EslintWebpackPlugin({
            context:path.resolve(__dirname,'../src'),
            exclude:'node_modules',
            cache:true,
            cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template:path.resolve(__dirname,"../public/index.html"),
        }),
        new ReactRefreshWebpackPlugin()
    ],
    mode:"development",
    devtool:"cheap-module-source-map",
    optimization:{
        splitChunks:{
            chunks:"all" //代码分割
        },
        runtimeChunk:{
            name:entrypoint=>`runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        }
    },
    //webpack解析块加载选项
    resolve:{
        //自动补全文件拓展名
        extensions:[".jsx",".js",".json"]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true//解决前端路由刷新404的问题
    }
}

webpack.prod.js

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const TerserWebpackPlugin = require("terser-webpack-plugin")
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
//返回处理样式的loader函数
let getStyleLoaders = function (pre) {
    return [
        MiniCssExtractPlugin.loader, "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            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][ext][query]",
        clean: true
    },
    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: {
                    dataCondition: {
                        maxSize: 10 * 1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test: /\.(woff2?|ttf)$/,
                type: "asset/resource",
            },
            //处理js
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, '../src'),
                loader: "babel-loader",
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                }
            }
        ]
    },
    plugins: [
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:10].css',
            chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
        }),
        new CopyWebpackPlugin({//复制到dist文件下
            patterns: [
                {
                    from: path.resolve(__dirname, "../public"), to: "../dist", globOptions: {
                        ignore: ["**/index.html", "**/ignored-directory/**"],//忽略Index.html文件
                    },
                }
            ]
        })
    ],
    mode: "production",
    devtool: "cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all" //代码分割
        },
        runtimeChunk: {
            name: entrypoint => `runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        },
        minimizer: [
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin(),
            new ImageMinimizerWebpackPlugin({
                minimizer: {
                    implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ]
    },
    //webpack解析块加载选项
    resolve: {
        //自动补全文件拓展名
        extensions: [".jsx", ".js", ".json"]
    }
}

Reuse, merge into one file, and create a new file webpack.config.js

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const TerserWebpackPlugin = require("terser-webpack-plugin")
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
//获取cross-env定义的环境变量
const isProduction = process.env.NODE_ENV==="production"

//返回处理样式的loader函数
let getStyleLoaders = function (pre) {
    return [
        isProduction?MiniCssExtractPlugin.loader:'style-loader', "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
module.exports = {
    entry: "./src/main.js",
    output: {
        path: isProduction?path.resolve(__dirname, "../dist"):undefined,
        filename:isProduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
        chunkFilename: isProduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
        assetModuleFilename: "static/media/[hash:10][ext][query]",
        clean: true
    },
    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: {
                    dataCondition: {
                        maxSize: 10 * 1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test: /\.(woff2?|ttf)$/,
                type: "asset/resource",
            },
            //处理js
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, '../src'),
                loader: "babel-loader",
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                    plugins:[
                        !isProduction&&"react-refresh/babel"
                    ].filter(Boolean)//激活js文件HMR功能
                }
            }
        ]
    },
    plugins: [
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        isProduction && new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:10].css',
            chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
        }),
        isProduction && new CopyWebpackPlugin({//复制到dist文件下
            patterns: [
                {
                    from: path.resolve(__dirname, "../public"), to: "../dist", globOptions: {
                        ignore: ["**/index.html", "**/ignored-directory/**"],//忽略Index.html文件
                    },
                }
            ]
        }),
        !isProduction && new ReactRefreshWebpackPlugin()
    ].filter(Boolean),
    mode: isProduction?"production":"development",
    devtool: isProduction?'source-map':"cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all" //代码分割
        },
        runtimeChunk: {
            name: entrypoint => `runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        },
        //是否要进行压缩
        minimizer:isProduction,
        minimizer: [
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin(),
            new ImageMinimizerWebpackPlugin({
                minimizer: {
                    implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ]
    },
    //webpack解析块加载选项
    resolve: {
        //自动补全文件拓展名
        extensions: [".jsx", ".js", ".json"]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true//解决前端路由刷新404的问题
    }
}

 After the third-party library is introduced, the package file will become larger and larger, the loading speed will be slow, and the page loading performance will not be good. Let's configure the code splitting to package separately:

optimization: {
        splitChunks: {
            chunks: "all", //代码分割
            cacheGroups:{
                react:{ //react react-dom react-router-dom 一起打包成一个js文件
                    test:/[\\/]node_modules[\\/]react(.*)?[\\/]/,
                    name:'chunk-react',
                    priority:40, //打包优先级权重
                },
                // antd:{ //antd单独打包
                //     test:/[\\/]node_modules[\\/]antd[\\/]/,
                //     name:'chunk-antd',
                //     priority:30,
                // },
                lib:{ //node_modules单独打包
                    test:/[\\/]node_modules[\\/]/,
                    name:'chunk-lib',
                    priority:20,
                },
            }
        },
}

Afterwards, the third-party library can be introduced to configure the package by itself, and judge whether it needs to be packaged separately according to the actual situation, considering whether the volume is too large and whether the number of requests is too large. If there is a warning saying that the packaging volume is too large, we don’t want to see it, so we add the performance attribute to false, so that performance analysis will not be performed and a warning will be issued, which can improve the packaging speed.

devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true//解决前端路由刷新404的问题
    },
    performance:false //关闭性能分析,提高打包速度

 Turn on the internal cache of webpack, which will help save time for the next packaging compilation:

cache: { //可开启webpack5内置缓存
        type: 'filesystem',
        allowCollectingMemory: true
    }

The final configuration of webpack.config.js is as follows:

const path = require("path")
const EslintWebpackPlugin = require("eslint-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin")
const TerserWebpackPlugin = require("terser-webpack-plugin")
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
//获取cross-env定义的环境变量
const isProduction = process.env.NODE_ENV

//返回处理样式的loader函数
let getStyleLoaders = function (pre) {
    return [
        isProduction?MiniCssExtractPlugin.loader:'style-loader', "css-loader", {
            //处理兼容性问题,配合package.json中的browserslist来指定兼容性做到什么程度
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                }
            }
        },
        pre
    ].filter(Boolean)
}
module.exports = {
    entry: "./src/main.js",
    output: {
        path: isProduction?path.resolve(__dirname, "../dist"):undefined,
        filename:isProduction?"static/js/[name].[contenthash:10].js":"static/js/[name].js",
        chunkFilename: isProduction?"static/js/[name].[contenthash:10].chunk.js":"static/js/[name].chunk.js",
        assetModuleFilename: "static/media/[hash:10][ext][query]",
        clean: true
    },
    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: {
                    dataCondition: {
                        maxSize: 10 * 1024, //小于10kb以下可以转为base64,减少请求数量
                    }
                }
            },
            // 处理其他资源
            {
                test: /\.(woff2?|ttf)$/,
                type: "asset/resource",
            },
            //处理js
            {
                test: /\.jsx?$/,
                include: path.resolve(__dirname, '../src'),
                loader: "babel-loader",
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                    plugins:[
                        !isProduction&&"react-refresh/babel"
                    ].filter(Boolean)//激活js文件HMR功能
                }
            }
        ]
    },
    plugins: [
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache')
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        isProduction && new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:10].css',
            chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
        }),
        isProduction && new CopyWebpackPlugin({//复制到dist文件下
            patterns: [
                {
                    from: path.resolve(__dirname, "../public"), to: "../dist", globOptions: {
                        ignore: ["**/index.html", "**/ignored-directory/**"],//忽略Index.html文件
                    },
                }
            ]
        }),
        !isProduction && new ReactRefreshWebpackPlugin()
    ].filter(Boolean),
    mode: isProduction?"production":"development",
    devtool: isProduction?'source-map':"cheap-module-source-map",
    optimization: {
        splitChunks: {
            chunks: "all", //代码分割
            cacheGroups:{
                react:{ //react react-dom react-router-dom 一起打包成一个js文件
                    test:/[\\/]node_modules[\\/]react(.*)?[\\/]/,
                    name:'chunk-react',
                    priority:40, //打包优先级权重
                },
                // antd:{ //antd单独打包
                //     test:/[\\/]node_modules[\\/]antd[\\/]/,
                //     name:'chunk-antd',
                //     priority:30,
                // },
                lib:{ //node_modules单独打包
                    test:/[\\/]node_modules[\\/]/,
                    name:'chunk-lib',
                    priority:20,
                },
            }
        },
        runtimeChunk: {
            name: entrypoint => `runtime~${entrypoint.name}.js` //单独储存引用的chunk的打包地址,main.js不会随着其他模块的变化导致地址变化而打包变化
        },
        //是否要进行压缩
        minimizer:isProduction,
        minimizer: [
            new CssMinimizerWebpackPlugin(),
            new TerserWebpackPlugin(),
            new ImageMinimizerWebpackPlugin({
                minimizer: {
                    implementation: ImageMinimizerWebpackPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ]
    },
    //webpack解析块加载选项
    resolve: {
        //自动补全文件拓展名
        extensions: [".jsx", ".js", ".json"]
    },
    devServer:{
        host:"localhost",
        port:3000,
        open:true,
        hot:true,
        historyApiFallback:true//解决前端路由刷新404的问题
    },
    performance:false, //关闭性能分析,提高打包速度。
    cache: { //可开启webpack5内置缓存
        type: 'filesystem',
        allowCollectingMemory: true
    }
}

That's basically it, the rest of the configuration can be configured by yourself during the development process.

Guess you like

Origin blog.csdn.net/m0_59962790/article/details/130047726