首页 > 解决方案 > 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”

当我更改样式时有效,但是当我更改有关组件的某些内容时无效。

标签: javascriptnode.jsreactjsexpresswebpack

解决方案


推荐阅读