使用Webpack5搭建项目(react篇)

本篇承接 使用Webpack5搭建项目 

开发模式

配置webpack.dev.js

新建项目,生成package.json文件,创建config文件夹,创建webpack.dev.js文件

配置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')
        })
    ]
}

配置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"
  ]
}

创建并配置.eslintrc.js

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

继续配置处理js文件

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

同时配置babel.config.js文件

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

添加HtmlWebpackPlugin,配置mode,以及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"
}

配置代码分割:

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

配置devServer:

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

这就是目前的基本配置:

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
    }
}

创建并配置main.js文件:

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

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

创建并配置App.jsx

import React from "react";

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

export default App

创建并配置public/index.html文件

<!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>

开始下载依赖:

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

 下载完成后,配置package.jason

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

输入指令:npm start ,页面报错了:

 这里需要babel-preset-app要求我们制定一个NODE_ENV或者BABEL_ENV的环境变量,我们下载cross-env的库设置环境变量:

npm install --save-dev cross-env

修改package.json:

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

输入npm start ,继续报错:

在引入的时候我们没有这里是对js文件有自动补全的功能,但这里是jsx文件,所以继续配置webpack.dev.js 

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

再次编译就没有问题了。

检测HMR热更新,样式文件是可以触发,但是js文件依旧不行,所以引用插件,react-refresh-webpack-plugin

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

配置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()
    ],
    ...
}

 接下来是路由设置: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>
  )
}

页面重新启动即可,但是刷新页面会有404的问题,解决方案:historyApiFallback,配置webapck.dev.js

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

希望路由单独打包,需要配置路由懒加载:

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

同时可以给指定魔法命名,可以打包成你想要的名称:

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

关于favicon.ico的配置,将文件放入public文件夹下面,然后配置index.html,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>

 生产模式:

我们直接复制一份dev的,修改名称为webpack.prod.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
    },

 处理css,单独打包文件并压缩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()
        ]
    },
    ...
}

处理压缩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()
        ]
    },

修改mode为production,devtool改为:source-map,删除devServer,删除HMR功能,

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"]
    },
}

压缩图片:

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",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ]

下载依赖:

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

修改package.json打包启动指令:

"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"
  },

开始打包npm run build即可。但是发现favicon没有被打包到dist下,使用插件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文件
                    },
                }
            ]
        })
    ],

重新打包后即可。


配置好了生产和开发,由于有很多地方是重复代码那么我们现在做一下整合,目前文件代码如下:

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"]
    }
}

进行复用,合并为一个文件,新建文件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的问题
    }
}

 在引入第三方库之后,打包文件会越来越大,加载速度会慢,页面加载性能不好,我们配置一下代码分割时单独打包:

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,
                },
            }
        },
}

之后引入第三方库可以自行配置打包,根据实际情况来判断是否需要单独打包,考虑体积是否过大,请求数量是否过多。如果遇上警告说打包体积太大,我们也不想看,就添加performance属性为false,就不会在做性能分析并报警告,可以提升打包速度。

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

 开启webpack内部缓存,有助于下次的打包编译节省时间:

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

最终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

//返回处理样式的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
    }
}

基本就是这样,其余的配置在开发过程中可自行配置。

猜你喜欢

转载自blog.csdn.net/m0_59962790/article/details/130047726