使用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 ,页面报错了:

338fc94709434eef8e29428fdfc9ee92.png

 这里需要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 ,继续报错:

51ad32bb4792416e8cac971291077dd0.png

在引入的时候我们没有这里是对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
    }
}

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

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,首先你需要安装webpack5、react、typescript、eslint以及相关的loader和插件。你可以通过以下命令安装它们: ``` npm install webpack webpack-cli webpack-dev-server react react-dom @types/react @types/react-dom typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks prettier -D ``` 接下来,你需要创建一个webpack配置文件,可以命名为`webpack.config.js`,在该文件中配置webpack相关内容: ```javascript const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.tsx', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, resolve: { extensions: ['.tsx', '.ts', '.js'], }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript', ], }, }, { loader: 'ts-loader', }, ], }, { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ['babel-loader'], }, { test: /\.css$/, use: ['style-loader', 'css-loader'], }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', }), ], devtool: 'inline-source-map', devServer: { contentBase: './dist', port: 3000, }, mode: 'development', }; ``` 在上面的配置中,我们指定了入口文件为`src/index.tsx`,打包后的输出文件为`dist/bundle.js`。我们还通过resolve属性指定了文件的拓展名,这样在引入文件时就不用指定拓展名了。 在module属性中,我们定义了不同类型文件的loader,例如对于`.tsx`和`.ts`文件,我们使用了`babel-loader`和`ts-loader`,对于`.js`和`.jsx`文件,我们只使用了`babel-loader`。此外,我们还使用了`style-loader`和`css-loader`处理`.css`文件。 在plugins属性中,我们使用了`HtmlWebpackPlugin`插件,用于生成HTML文件。在devServer属性中,我们指定了开发服务器的端口和从哪个文件夹提供内容。 最后,我们使用了`inline-source-map`作为开发模式下的source-map,将模式设置为`development`。 接下来,你需要创建一个`.babelrc`文件,用于配置babel: ```json { "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"] } ``` 然后,你需要创建一个`.eslintrc.json`文件,用于配置eslint: ```json { "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": ["airbnb", "plugin:@typescript-eslint/recommended", "prettier"], "rules": { "prettier/prettier": ["error"], "react/jsx-filename-extension": [1, { "extensions": [".tsx"] }], "import/extensions": ["error", "never", { "svg": "always" }] }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".jsx", ".ts", ".tsx"] } } } } ``` 在上面的配置中,我们使用了`@typescript-eslint/parser`解析Typescript代码,并使用了`@typescript-eslint/eslint-plugin`插件提供的规则。我们还继承了`eslint-config-airbnb`和`eslint-config-prettier`,并使用了一些自定义的规则。 最后,你需要在`package.json`中添加一些scripts,用于启动开发服务器和打包代码: ```json { "scripts": { "start": "webpack serve --mode development", "build": "webpack --mode production" } } ``` 现在你就可以使用`npm start`命令启动开发服务器,使用`npm run build`命令打包代码了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Goat恶霸詹姆斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值