欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 手动搭建并配置react项目(webpack5)

手动搭建并配置react项目(webpack5)

2025/5/6 1:13:01 来源:https://blog.csdn.net/yalywq/article/details/146602974  浏览:    关键词:手动搭建并配置react项目(webpack5)

手动搭建并配置react项目(webpack5)

介绍

不使用脚手架,利用webpack,手动搭建react项目框架

1、项目创建

创建目录 react_wepack

2、webpack+ react基础架构

2.1 配置 webpack.dev.js

基础配置说明可参考这篇文章

配置 loader、plugin、eslint【见webpack.dev.js】
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
const { DefinePlugin } = require('webpack');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');// 用来处理获取的样式
function getStyleLoaders(pre) {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {// plugins: [["autoprefixer"]],plugins: ['postcss-preset-env'],//能解决大多数兼容性问题},},}, pre].filter(Boolean);
}module.exports = {mode: "development", // 开发模式entry: path.resolve(__dirname, "../src/main.js"), // 入口文件 相对路径// web serverdevtool: 'cheap-module-source-map', //build: slow rebuild: fast// 在dist中不会输出devServer: {static: {directory: path.resolve(__dirname, "../dist"), // 打包后的文件路径 directory:目录},open: true, //自动打开浏览器compress: true, //启动gzip压缩port: 9000, // 端口号hot: true,// 提供HMR功能,只更新某个模块,没有替换整个项目},output: {clean: true, // 清理 /dist 文件夹// filename: "js/main.js", // 打包后的文件名称filename: "js/[name].[contenthash:8].js", // 打包后的文件名称path: undefined,// 打包输出的其他文件名称chunkFilename: "js/[name].[contenthash:8].chunk.js",// 图片 等字体通过type: asset处理资源命名方式assetModuleFilename: 'media/[name].[contenthash:8][ext][query]',},cache: {type: 'filesystem',allowCollectingMemory: true,idleTimeout: 60000,compression: 'gzip',},// 开发环境不用处理optimization: {minimize: true, // 强制启用压缩// 对代码进行分割splitChunks: {chunks: 'all',},// runtimeChunk: 'single',minimizer: [new CssMinimizerPlugin(),]},plugins: [new HtmlWebpackPlugin({// 模版:以public/index.html为模板生成打包后的index.htmltemplate: path.resolve(__dirname, "../public/index.html"),// BASE_URL: process.env.BASE_URL || '/'}),new ESLintPlugin({// 配置哪些目录需要检查context: path.resolve(__dirname, './src'),exclude: 'node_modules',// 不写 默认也有cache: true,// 开启缓存npmcacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),// threads,// 开启多进程打包eslintPath:'eslint',//指定传统}),new MiniCssExtractPlugin({filename: "css/[name].[contenthash:8].css"}),new DefinePlugin({// window.ENV = 'production'ENV: JSON.stringify('development'),BASE_URL: '"../"' // 定义全局变量BASE_URL}),new CssMinimizerPlugin(),],// loader 加载器module: {rules: [// 每个文件只有一个loader配置处理{oneOf: [{test: /\.(gif|png|jpe?g)$/i,type: "asset",parser: {dataUrlCondition: {// 小于10kb的图片转成base64,减少请求数量// 缺点:体积会大一点maxSize: 10 * 1024, // 小于10kb},},generator: {// 输出图片的名称filename: "imgs/[name].[contenthash:8][ext]",},},{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件// 对文件原封不动的输出type: "asset/resource",// generator: {//   filename: "media/[name].[contenthash:8][ext]",// },},{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体type: "asset/resource",// generator: {//   filename: "fonts/[name].[contenthash:8][ext]"// }},// 复杂场景用{test: /\.jsx?$/,exclude: /node_modules/,use: [{loader: 'babel-loader',options: {presets: [['@babel/preset-react', { runtime: 'automatic' }]],cacheDirectory: true, // 开启babel缓存cacheCompression: false,// 关闭缓存文件压缩plugins: ['@babel/plugin-transform-runtime'],},},]},{test: /\.css$/,// MiniCssExtractPlugin.loader 最终会将css提取到单独的文件use: getStyleLoaders(), // 从右向左解析原则// use: ["style-loader", "css-loader"]},{test: /\.less$/,use: getStyleLoaders("less-loader"), // 从右向左解析原则// use: ["style-loader", "css-loader", "less-loader"]},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader")},{test: /\.styl$/,use: getStyleLoaders("stylus-loader")},]}],},// webpack解析加载块加载选项resolve:{extensions: ['.jsx','.js','.json',],}
};

2.2 新建eslintrc.js文件

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

2.3 新建babel.config.js文件

module.exports = {persets:["react-app"]
}

2.4 创建 package.json 文件

npm init -y

2.5 创建 react main.js App.jsx

  • 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';
const App = ()=>{return(<div><h1>Hello World</h1></div>)
}

3、安装依赖

npm install  webpack webpack-cli  webpack-dev-server  --D
npm install mini-css-extract-plugin  html-webpack-plugin eslint-webpack-plugin --D
npm install style-loader css-loader less-loader sass sass-loader  postcss-loader  postcss-preset-env stylus-loader --D
npm install babel-loader @babel/core babel-preset-react-app   --Dnpm install eslit eslint-config-react-app --D
npm install react react-dom --S

4、配置package.json webpack打包入口

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

5、public index.html 创建一个id为app节点,以便挂载react组件

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

5、 报错处理

  • 报错一 eslint 版本太高
   [eslint] Couldn't find FlatESLint, you might need to set eslintPath to 'eslint/use-at-your-own-risk'

解决方案: 换成低版本的eslint

  • 报错二 提示有未使用的变量NODE_ENV or BABEL_ENV
    Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV
    解决方案:
  1. 安装cross-env
npm install  cross-env --D
  1. 修改package.json中的dev命令,定义环境变量
 "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  • 报错三’./App’ 未加后缀,代码不认识

ERROR in ./src/main.js 4:0-24
Module not found: Error: Can’t resolve ‘./App’ in ‘E:\study_2025\react-webpack5\src’

解决方案:配置resolve,告诉webpack,先用.jsx解析,再用.js解析,再用.json解析

  // webpack解析加载块加载选项resolve:{extensions: ['.jsx','.js','.json',],}
  • 报错四:
    Module not found: Error: Can’t resolve 'E:\study_2025\react-webpack5\public\index.html
    检查了webpack的配置文件,发现没有配置html-webpack-plugin也是正确的
    看视频发现没有这一步的配置
    自己手动创建了public目录和index.html文件
    掉了第五步: 5、创建public目录和 index.html文件, 创建一个id为app节点,以便挂载react组件
<body><div id="app"></div>
</body>
  • 异常五:页面启动后无报错,页面显示空白
    原因: 在高版本中import react from ‘react’ 不用写,打包会报错
    解决更新 babel.config.js 或 webpack.config.js:
// babel.config.js
module.exports = {presets: [['@babel/preset-react',{runtime: 'automatic', // 启用自动 JSX 转换importSource: 'react', // 默认值,可改为 'preact' 等其他库},],],
};

或者

// webpack.config.js
module: {rules: [{test: /\.(js|jsx)$/,use: {loader: 'babel-loader',options: {presets: [['@babel/preset-react', { runtime: 'automatic' }]]}}}]
}

我用的下面这种

6、最终的目录结构

react-webpack5
├── public/ # 手动创建此文件夹
│ ├── index.html # 主 HTML 模板
│ ├── favicon.ico # 网站图标

├── src/ # 源码目录
│ ├── main.js # 入口文件
│ └── App.jsx # 应用根组件
└── config
└──── webpack.dev.js # Webpack 开发配置文件

7、webpack优化

配置热更新

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

在webpack.dev.js中配置热更新


const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
module.exports = {module:{devServer: {···hot: true,// 开启HMR},rules:[{test: /\.jsx?$/,...options: {...plugins: ["react-refresh/babel", // 激活js的HMR ,仅用于开发环境],},},]}
}plugins: [new ReactRefreshWebpackPlugin(), // 激活js的HMR
]

8、配置react路由

  1. 安装react-router-dom
npm install react-router-dom --S
  1. 在main.js中引入react-router-dom
import { BrowserRouter } from 'react-router-dom'const root = ReactDom.createRoot(document.getElementById("app"));
root.render(<BrowserRouter><App /></BrowserRouter>);
  1. 在pages文件夹下创建 About.jsx 、Home.jsx 文件
  2. 在App.jsx中配置路由跳转
import { Routes, Route, Link } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
const App = () => {return (<div><ul><li><Link to="/home">首页</Link></li><li><Link to="/about">关于</Link></li></ul><Routes><Route path="/home" element={<Home/>} /><Route path="/about" element={<About/>} /></Routes></div>)
}
export default App;
  1. 路由强制刷新出现Cannot GET /about
    解决方案:
module.exports = {devServer: {...port: 9000, // 端口号hot: true,// 提供HMR功能, 只更新某个模块,没有替换整个项目historyApiFallback: true, // 解决前端路由刷新404问题},

9、若想让文件单独打包,可配置路由懒加载

import React, { lazy, Suspense } from 'react';
import { Routes, Route, Link } from 'react-router-dom';
const LayAbout = lazy(() => import('./pages/About'));
const LayHome = lazy(() => import('./pages/Home'))
// const Home = lazy(() => import(/* webpackChunkName: 'home' */ "./pages/Home"));
// const About = lazy(() => import(/* webpackChunkName: 'about' */ "./pages/About"));const App = () => {return (<div><ul><li><Link to="/home">首页</Link></li><li><Link to="/about">关于</Link></li></ul><Suspense fallback={<div>页面正在加载中...</div>}><Routes ><Route path="/home" element={<LayHome />} /><Route path="/about" element={<LayAbout />} /></Routes></Suspense></div>)
}
export default App;

10、webpack.dev 配置文件完整

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
const { DefinePlugin } = require('webpack');
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");// 用来处理获取的样式
function getStyleLoaders(pre) {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {// plugins: [["autoprefixer"]],plugins: ['postcss-preset-env'],//能解决大多数兼容性问题},},}, pre].filter(Boolean);
}module.exports = {mode: "development", // 开发模式entry: path.resolve(__dirname, "../src/main.js"), // 入口文件 相对路径// web serverdevtool: 'cheap-module-source-map', //build: slow rebuild: fast// 在dist中不会输出devServer: {static: {directory: path.resolve(__dirname, "../dist"), // 打包后的文件路径 directory:目录},open: true, //自动打开浏览器compress: true, //启动gzip压缩port: 9000, // 端口号hot: true,// 提供HMR功能, 只更新某个模块,没有替换整个项目},output: {clean: true, // 清理 /dist 文件夹// filename: "js/main.js", // 打包后的文件名称filename: "js/[name].[contenthash:8].js", // 打包后的文件名称path: undefined,// 打包输出的其他文件名称chunkFilename: "js/[name].[contenthash:8].chunk.js",// 图片 等字体通过type: asset处理资源命名方式assetModuleFilename: 'media/[name].[contenthash:8][ext][query]',},cache: {type: 'filesystem',allowCollectingMemory: true,idleTimeout: 60000,compression: 'gzip',},// 开发环境不用处理optimization: {minimize: true, // 强制启用压缩// 对代码进行分割splitChunks: {chunks: 'all',},},plugins: [new HtmlWebpackPlugin({// 模版:以public/index.html为模板生成打包后的index.htmltemplate: path.resolve(__dirname, "../public/index.html"),// BASE_URL: process.env.BASE_URL || '/'}),new ESLintPlugin({// 配置哪些目录需要检查context: path.resolve(__dirname, './src'),exclude: 'node_modules',// 不写 默认也有cache: true,// 开启缓存npmcacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),// threads,// 开启多进程打包eslintPath: 'eslint',//指定传统}),new MiniCssExtractPlugin({filename: "css/[name].[contenthash:8].css"}),new DefinePlugin({// window.ENV = 'production'ENV: JSON.stringify('development'),BASE_URL: '"../"' // 定义全局变量BASE_URL}),new CssMinimizerPlugin(),new ReactRefreshWebpackPlugin(), // 激活js的HMR],// loader 加载器module: {rules: [// 每个文件只有一个loader配置处理{oneOf: [{test: /\.(gif|png|jpe?g)$/i,type: "asset",parser: {dataUrlCondition: {// 小于10kb的图片转成base64,减少请求数量// 缺点:体积会大一点maxSize: 10 * 1024, // 小于10kb},},generator: {// 输出图片的名称filename: "imgs/[name].[contenthash:8][ext]",},},{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件// 对文件原封不动的输出type: "asset/resource",// generator: {//   filename: "media/[name].[contenthash:8][ext]",// },},{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体type: "asset/resource",// generator: {//   filename: "fonts/[name].[contenthash:8][ext]"// }},// 复杂场景用{test: /\.jsx?$/,exclude: /node_modules/,use: [{loader: 'babel-loader',options: {presets: [['@babel/preset-react', { runtime: 'automatic' }]],cacheDirectory: true, // 开启babel缓存cacheCompression: false,// 关闭缓存文件压缩plugins: ['@babel/plugin-transform-runtime',"react-refresh/babel", // 激活js的HMR],},},]},{test: /\.css$/,// MiniCssExtractPlugin.loader 最终会将css提取到单独的文件use: getStyleLoaders(), // 从右向左解析原则// use: ["style-loader", "css-loader"]},{test: /\.less$/,use: getStyleLoaders("less-loader"), // 从右向左解析原则// use: ["style-loader", "css-loader", "less-loader"]},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader")},{test: /\.styl$/,use: getStyleLoaders("stylus-loader")},]}],},// webpack解析加载块加载选项resolve: {extensions: ['.jsx', '.js', '.json',],}
};

生产环境配置

  • 复制一份webpack.dev.js文件,并改名为webpack.config.prod.js
  • 修改项
  mode: "production", // 生产模式devtool: "source-map",
  • 添加插件css-minimizer-webpack-plugin、mini-css-extract-plugin
  1. css 压缩
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');optimization: {...minimizer: [new CssMinimizerPlugin(),]},
  1. js 压缩
 const TerserWebpackPlugin = require("terser-webpack-plugin");...
optimization: {...minimizer: [new TerserWebpackPlugin(),]
},
  1. 移除HMR功能

    • 移除devServer
    • ReactRefreshWebpackPlugin移除
    • plugins: [“react-refresh/babel”,] // 激活js的HMR
  2. 对图片进行压缩

  • 安装依赖
  npm i image-minimizer-webpack-plugin --Dnpm install imagemin-gifsicle  imagemin-jpegtran imagemin-optipng imagemin-svgo --D

包不好下

      new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),
  1. 将public目录下的静态资源复制到dist目录下
// const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),globOptions: {// 忽略index.html文件ignore: ["**/index.html"],},},],}),

webpack 公共

  • 引入antd 三方组件
  1. 安装
npm install antd --S
import { Button } from 'antd';<Button type="primary">按钮</Button>

webpack 修改某个主题的颜色

  1. 页面
import { Button } from 'antd';<Button type="primary">按钮</Button>
  1. 在 webpack.prod.js 文件中添加以下代码
const getStyleLoaders = (pre) => {return [...pre && {loader: pre,options:pre === "less-loader"? {// antd自定义主题配置// 主题色文档:https://ant.design/docs/react/customize-theme-cn#Ant-Design-%E7%9A%84%E6%A0%B7%E5%BC%8F%E5%8F%98%E9%87%8FlessOptions: {modifyVars: { "@primary-color": "red" },javascriptEnabled: true,},}: {},},].filter(Boolean);
};
  • webpack 打包时部分包比较大,这个时候可以使用 webpack-bundle-analyzer 来分析打包后的包大小,然后进行优化。
  optimization: {splitChunks: {chunks: "all",cacheGroups: {// react react-dom react-router-dom 一起打包成一个js文件react: {test: /[\\/]node_modules[\\/]react(.*)?[\\/]/,name: "chunk-react",priority: 40, // 权重最大},// antd 单独打包antd: {test: /[\\/]node_modules[\\/]antd[\\/]/,name: "chunk-antd",priority: 30,},// 剩下node_modules单独打包libs: {test: /[\\/]node_modules[\\/]/,name: "chunk-libs",priority: 20,},},},}

点击查看项目地址

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词