Webpack+Express搭建的React项目修改文件后页面不刷新问题
热重载失效问题排查结果
一共5个核心配置错误,逐个修复即可恢复热更新能力:
- Webpack入口缺少热更新客户端脚本
webpack-hot-middleware需要在入口注入客户端运行时,才能和服务端建立热更新通信连接,当前entry只配置了React入口文件,没有添加HMR客户端脚本,浏览器无法接收热更新推送。
- Webpack入口缺少热更新客户端脚本
- Webpack output缺少必填的
publicPath配置
server.js中读取webpackConfig.output.publicPath传给热更新中间件,但配置里未定义该字段,会导致热更新资源路径解析错误,同时webpack-dev-middleware也依赖这个字段定位内存中的编译产物。
- Webpack output缺少必填的
- Express中间件/路由顺序错误
静态资源服务、根路由响应逻辑写在了webpack开发中间件、热更新中间件的前面,Express按注册顺序执行中间件,开发环境下请求会优先命中磁盘上dist目录的旧静态文件,根本访问不到webpack-dev-middleware存在内存里的最新编译产物,也触发不了热更新逻辑。另外res.sendFile参数写法错误,多传参数会导致文件响应异常。
- Express中间件/路由顺序错误
webpack-dev-middleware缺少publicPath配置
这个配置必须和webpack output里的publicPath保持一致,否则内存文件的服务路由匹配失败。
- React18渲染API适配问题
当前用的是React18版本,旧的ReactDOM.render写法在HMR触发时可能出现渲染异常、状态丢失问题,推荐换成React18官方的createRootAPI。
- React18渲染API适配问题
修正后的配置文件
webpack.config.js
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const webpack = require("webpack"); module.exports = { mode: "development", entry: { // 注入热更新客户端脚本,reload=true表示热更新失败时自动刷新页面 app: ["webpack-hot-middleware/client?reload=true", "./src/hello.jsx"], }, module: { rules: [ // 新增exclude排除node_modules,大幅提升编译速度 { test: /\.(js|jsx)$/, use: ["babel-loader"], exclude: /node_modules/ }, { test: /\.(css)$/, use: ["style-loader", "css-loader"] }, { test: /\.(jpg|png|jpeg)$/, use: ["file-loader"] }, { test: /\.(html)$/, use: ["html-loader"] }, ], }, plugins: [ new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ template: "./src/hello.html", filename: "index.html", chunks: ["app"], }), ], output: { filename: "[name].bundle.js", path: path.resolve(__dirname, "dist"), publicPath: "/", // 必须配置,和dev-middleware保持一致 }, devtool: "eval-cheap-module-source-map", // 开发环境加source-map方便调试 };
server.js
const express = require("express"); const app = express(); const path = require("path"); const PORT = process.env.PORT || 3000; const isProd = process.env.NODE_ENV === "production"; if (!isProd) { const webpack = require("webpack"); const webpackConfig = require("./webpack.config"); const compiler = webpack(webpackConfig); // 开发环境的webpack中间件必须放在路由、静态服务前面 app.use(require("webpack-dev-middleware")(compiler, { publicPath: webpackConfig.output.publicPath, // 必须和output.publicPath一致 })); app.use( require("webpack-hot-middleware")(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath, }) ); } // 只有生产环境才启用磁盘静态文件服务,避免开发环境命中旧文件 if (isProd) { app.use(express.static(path.join(__dirname, "dist"))); } // 路由放在中间件后面 app.get("/", (req, res) => { // 修正sendFile参数,传入完整文件路径 res.sendFile(path.join(__dirname, "dist/index.html")); }); app.listen(PORT, () => { console.log(`listening at localhost:${PORT}`); });
hello.jsx(适配React18,推荐修改)
import React from "react"; import { createRoot } from "react-dom/client"; import "./hello.css"; const Hello = () => { return ( <div> <h2>Hello React</h2> </div> ); }; const root = createRoot(document.getElementById("root")); root.render(<Hello />); // 热更新接收逻辑 if (module.hot) { module.hot.accept(); }
package.json补充说明
现有依赖没有缺失,不需要额外安装包。如果需要明确区分开发/生产环境,可以把start脚本按系统调整:
- Windows系统:
"start": "set NODE_ENV=development&&node server.js" - Mac/Linux系统:
"start": "NODE_ENV=development node server.js"
默认不设置NODE_ENV时也会走开发逻辑,不影响热更新生效。
所有配置修改完成后,重启node服务,再修改React组件代码即可触发热更新,无需手动刷新页面。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

