You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js+Express的React项目Webpack CSS加载问题求助

React SSR(Express + Node.js)CSS加载失败解决方案

问题背景

使用Node.js + Express搭建React服务端渲染应用时,组件可正常渲染,但CSS始终无法加载。相关代码如下:

Webpack配置

const path = require('path');
const nodeExternals = require('webpack-node-externals');

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  target: 'node',
  mode: 'production',
  externals: [nodeExternals()],
  entry: './server/index.js',
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    // new MiniCssExtractPlugin(),
  ],  
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: 'style-loader',
            options: { 
                insert: 'head', // insert style tag inside of <head>
                injectType: 'singletonStyleTag' // this is for wrap all your style in just one style tag
            },
          },
          "css-loader",
        ],
        sideEffects: true, 
        include: __dirname
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react',
            ],
          },
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css'],
    modules: [
      'node_modules'
    ]
  },
};

服务端代码(server/index.js)

const express = require('express');
const app = express();
const port = 5005;
const path = require('path');
var ReactDomServer = require('react-dom/server');
import Hello from '../ui.src/hello';
var cors = require('cors');
const axios = require('axios');
// import '../ui.src/assets/css/style.css';

app.use(cors())

app.get("/", (req,res) => {
    const component = ReactDomServer.renderToString(<Hello />);
    res.send(`<html><body class='testclass'>${component}</body></html>`);
})

app.listen(port, () => {
    console.log(`Server now listening at http://localhost:${port}`)
})

React组件(ui.src/hello.jsx)

import React, { useEffect, useState } from 'react';
import './assets/css/style.css';

const Hello = () => {
    return (
      <h1>Hello World this is Test File....</h1>
    )
}

export default Hello;

核心问题分析

你的Webpack配置设置了target: 'node',打包后的代码运行在Node.js环境中,但你用了style-loader——这个loader的工作原理是在浏览器DOM中插入<style>标签,Node.js没有DOM环境,所以完全无法生效。


分步解决方案

1. 修改Webpack配置,改用MiniCssExtractPlugin提取CSS

把style-loader替换为MiniCssExtractPlugin.loader,启用插件将CSS提取为单独文件:

const path = require('path');
const nodeExternals = require('webpack-node-externals');

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  target: 'node',
  mode: 'production',
  externals: [nodeExternals()],
  entry: './server/index.js',
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'build'),
  },
  plugins: [
    // 启用插件,生成带哈希的CSS文件(利于缓存)
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'
    }),
  ],  
  optimization: {
    minimizer: [
      `...`, // 保留默认JS压缩器
      new CssMinimizerPlugin(), // 压缩CSS
    ],
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          MiniCssExtractPlugin.loader, // 替换style-loader
          "css-loader",
        ],
        sideEffects: true, // 保留,防止Webpack树摇掉CSS
        include: __dirname
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react',
            ],
          },
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css'],
    modules: [
      'node_modules'
    ]
  },
};

2. 服务端托管静态资源并注入CSS到HTML

  • 让Express托管build目录下的静态资源(包括提取出的CSS)
  • 在渲染的HTML模板中插入CSS文件的链接

修改后的服务端代码:

const express = require('express');
const app = express();
const port = 5005;
const path = require('path');
const ReactDomServer = require('react-dom/server');
import Hello from '../ui.src/hello';
const cors = require('cors');
const axios = require('axios');

app.use(cors())
// 托管build目录下的静态资源(CSS、图片等)
app.use(express.static(path.resolve(__dirname, '../build')));

app.get("/", (req,res) => {
    const component = ReactDomServer.renderToString(<Hello />);
    // 插入CSS链接到<head>中,文件名对应MiniCssExtractPlugin的输出
    res.send(`
    <html>
      <head>
        <link rel="stylesheet" href="/main.[contenthash].css">
      </head>
      <body class='testclass'>${component}</body>
    </html>`);
})

app.listen(port, () => {
    console.log(`Server now listening at http://localhost:${port}`)
})

3. 进阶优化(可选)

如果要避免哈希值变化导致的引用错误,可以使用webpack-manifest-plugin生成manifest.json,在服务端读取该文件动态获取CSS的实际路径:

  1. 安装插件:npm install webpack-manifest-plugin --save-dev
  2. Webpack配置中添加插件:
    const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
    // ...
    plugins: [
      new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' }),
      new WebpackManifestPlugin({ fileName: 'manifest.json' })
    ]
    
  3. 服务端读取manifest:
    const manifest = require('../build/manifest.json');
    // ...
    <link rel="stylesheet" href="/${manifest['main.css']}">
    

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:27:02