javascript - Webpack 的 HMR 坏了
问题描述
我按照步骤在我的 SSR React + Express APP 上添加了 webpack-hot-middleware,但它不起作用。
服务器.js
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import webpack from "webpack";
import helmet from "helmet";
import React from "react";
import { searchTrack } from "api/server";
import { renderToString } from "react-dom/server";
import { renderRoutes } from "react-router-config";
import { StaticRouter } from "react-router-dom";
import { Layout } from "components";
import serverRoutes from "../frontend/routes/serverRoutes";
import getManifest from "./getManifest";
dotenv.config();
const { ENV, PORT_DEV, PORT_PRO } = process.env;
const port = ENV === "development" ? PORT_DEV : PORT_PRO;
const app = express();
app.use(
cors({
origin: ["*"],
})
);
if (ENV === "development") {
console.log("#########################################");
console.log("Enviroment: ", "Working on develop");
const webpackConfig = require("../../webpack.config");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
const compiler = webpack(webpackConfig);
const serverConfig = {
port: PORT_DEV,
hot: true,
};
app.use(webpackDevMiddleware(compiler, serverConfig));
app.use(webpackHotMiddleware(compiler));
}
if (ENV === "production") {
app.use((request, response, next) => {
if (!request.hashManifest) {
request.hashManifest = getManifest();
}
next();
});
app.use(express.static(`${__dirname}/public`));
app.use(helmet.permittedCrossDomainPolicies());
app.disable("x-powered-by");
}
const setResponse = (html, manifest) => {
const mainStyles = manifest ? manifest["main.css"] : "assets/main.css";
const mainBuild = manifest ? manifest["main.js"] : "assets/main.js";
const vendorBuild = manifest ? manifest["vendors.js"] : "assets/vendor.js";
return `
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8" />
<link href="${mainStyles}" rel="stylesheet" type="text/css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lyrics.io</title>
</head>
<body>
<div id="root">${html}</div>
<script src="${mainBuild}" type="text/javascript"></script>
<script src="${vendorBuild}" type="text/javascript"></script>
</body>
</html>
`;
};
const renderApp = (request, response) => {
const html = renderToString(
<Layout>
<StaticRouter location={request.url} context={{}}>
{renderRoutes(serverRoutes)}
</StaticRouter>
</Layout>
);
response.send(setResponse(html, request.hashManifest));
};
app.get("/searchTracks/:search/:sort", (req, res) => {
const { search, sort } = req.params;
searchTrack(search, sort)
.then(
(response) =>
response.status === 200 &&
res.send(response.data.message.body.track_list)
)
.catch((error) => console.log(error));
});
app.get("*", renderApp);
app.listen(process.env.PORT || port, (error) => {
if (error) {
console.log("Error: ", "can not run the server.");
} else {
console.log(`Server running on port ${port} - ${ENV}`);
console.log("#########################################");
}
});
webpack.config.js
const path = require("path");
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
require("dotenv").config();
const isDev = process.env.ENV === "development";
const entry = ["./src/frontend/index.js"];
if (isDev) {
entry.push(
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true"
);
}
console.log(entry);
module.exports = {
devtool: !isDev ? "hidden-source-map" : "eval-cheap-source-map",
entry,
mode: process.env.ENV,
output: {
path: path.resolve(__dirname, "src/server/public"),
filename: isDev ? "assets/main.js" : "assets/main-[hash].js",
publicPath: "/",
},
resolve: {
alias: {
styles: path.resolve(__dirname, "./src/frontend/assets"),
components: path.resolve(__dirname, "./src/frontend/components"),
api: path.resolve(__dirname, "./src/api"),
},
extensions: [".js", ".jsx"],
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
splitChunks: {
chunks: "async",
name: true,
cacheGroups: {
vendors: {
name: "vendors",
chunks: "all",
reuseExistingChunk: true,
priority: 1,
filename: isDev ? "assets/vendor.js" : "assets/vendor-[hash].js",
enforce: true,
test(module, chunks) {
const name = module.nameForCondition && module.nameForCondition();
return chunks.some(
(chunk) =>
chunk.name !== "vendors" && /[\\/]node_modules[\\/]/.test(name)
);
},
},
},
},
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.styl$/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /\.(s*)css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
],
},
{
test: /\.(png|gif|jpg)$/,
use: [
{
loader: "file-loader",
options: {
name: "assets/[hash].[ext]",
},
},
],
},
],
},
devServer: {
historyApiFallback: true,
},
plugins: [
isDev ? new webpack.HotModuleReplacementPlugin() : () => {},
!isDev
? new CompressionWebpackPlugin({
test: /\.js$|\.css$/,
filename: "[file].gz",
})
: () => {},
!isDev ? new ManifestPlugin() : () => {},
new MiniCssExtractPlugin({
filename: isDev ? "assets/main.css" : "assets/main-[hash].css",
}),
],
};
babel.rc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"react-hot-loader/babel",
"babel-plugin-webpack-alias"
]
}
对 package.json 的依赖
"dependencies": {
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.4",
"@babel/register": "^7.11.5",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"asset-require-hook": "^1.2.0",
"axios": "^0.20.0",
"babel-loader": "^8.1.0",
"babel-plugin-webpack-alias": "^2.1.2",
"compression-webpack-plugin": "^6.0.3",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"helmet": "^4.1.1",
"history": "^5.0.0",
"husky": "^4.3.0",
"ignore-styles": "^5.0.1",
"mini-css-extract-plugin": "^0.12.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-hot-loader": "^4.13.0",
"react-icons": "^3.11.0",
"react-router": "^5.2.0",
"react-router-config": "^5.1.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3",
"style-loader": "^2.0.0",
"stylus": "^0.54.8",
"stylus-loader": "^4.1.1",
"terser-webpack-plugin": "^4.2.3",
"webpack": "^4.44.2",
"webpack-manifest-plugin": "^2.2.0"
},
"devDependencies": {
"css-loader": "^4.3.0",
"eslint": "^7.10.0",
"eslint-config-prettier": "^6.12.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.21.3",
"eslint-plugin-standard": "^4.0.1",
"file-loader": "^6.1.0",
"lint-staged": "^10.4.0",
"nodemon": "^2.0.4",
"path": "^0.12.7",
"prettier": "^2.1.2",
"webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.2",
"webpack-hot-middleware": "^2.25.0"
},
这是我启动以在开发模式下启动的命令
“开始开发”:“nodemon src/server/index.js”
当我更改样式时有效,但是当我更改有关组件的某些内容时无效。
解决方案
推荐阅读
- javascript - Node.js 进程在一些 ASP.NET Core 项目中消耗大量 CPU
- javascript - 使用JS匹配价格
- node.js - 为什么我的云功能在完成任务后还能继续运行?
- android - React-native:实施奖励推荐(邀请和赚取)
- android - 将显示和隐藏按钮添加到 BottomNavigationView
- ember.js - 与嵌套数据的自反关系
- javascript - 量角器异步/等待错误:未处理的承诺拒绝
- android - Android - Fused Location Provider Client 有时会得到错误的位置
- ssl - 如何将 www.sitename.com 变成 www2.sitename.com?
- javascript - 使用 Apollo 链路状态缓存实现客户端过滤