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

如何用splitChunks插件拆分Express/Nest项目中的CSS/SCSS为独立文件?

拆分Webpack主Chunk的CSS为多个独立文件(非懒加载场景)

需求背景

基于Express/Nest框架、React模板、Webpack构建的项目,需要将全局样式拆分生成多个独立CSS文件(如main.css、other1.css、other2.css),而非合并为单个main.css(第三方库样式除外)。需求源于需要区分不同场景的全局样式,例如PDF页面的专属规则:

@page {
  size: A4;
}

期望输出的dist目录结构:

dist
 -main.css
 -other1.css
 -other2.css

当前Webpack配置

const nxConfig = require('@nrwl/react/plugins/webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const autoprefixer = require('autoprefixer')

module.exports = (config) => {
  nxConfig(config)

  config.node = {
    global: true,
  }

  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        plugins: [autoprefixer],
      },
    },
  }

  const rawCssLoader = {
    loader: 'raw-css-loader',
  }

  let isFontsAdded = false
  const sassLoader = {
    loader: 'sass-loader',
    options: {
      additionalData: (content) => {
        if (!isFontsAdded) {
          isFontsAdded = true
          return '@import "libs/assets/theme/fonts/index.scss";' + content
        }
        return content
      },
    },
  }

  config.module.rules.push({
    test: /\.css$|\.scss$|\.sass$/,
    oneOf: [
      {
        test: /\.module\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]--[hash:base64:5]',
              },
              importLoaders: 1,
            },
          },
          postcssLoader,
        ],
      },
      {
        test: /\.module\.(scss|sass)$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]--[hash:base64:5]',
              },
              importLoaders: 3,
            },
          },
          postcssLoader,
          'resolve-url-loader',
          sassLoader,
        ],
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, rawCssLoader, postcssLoader],
      },
      {
        test: /\.scss$|\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: {
                compileType: 'icss',
                localIdentName: '[local]--[hash:base64:5]',
              },
            },
          },
          postcssLoader,
          'resolve-url-loader',
          sassLoader,
        ],
      },
    ],
  })

  config.plugins.push(
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    })
  )

  // load css assets
  config.module.rules.push({
    test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)(\?.*)?$/i,
    issuer: /(?<!module)\.scss$|\.sass$/,
    type: 'asset/inline',
  })

  config.module.rules.push({
    test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)(\?.*)?$/i,
    issuer: /\.scss$|\.sass$/,
    type: 'asset/inline',
  })

  return config
}

尝试过的方案(未成功)

尝试用splitChunks插件拆分,但无法捕获所有目标CSS文件:

config.optimization = {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          // cacheGroupKey here is `commons` as the key of the cacheGroup
          name(module, chunks, cacheGroupKey) {
          
            //CHECK CERTAIN CSS CHINKS HERE
            
            const moduleFileName = module
              .identifier()
              .split('/')
              .reduceRight((item) => item)
            const allChunksNames = chunks.map((item) => item.name).join('~')
            return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`
          },
          chunks: 'all',
        },
      },
    },
  }

核心疑问:能否通过splitChunks将生成main.css的主chunk拆分为多个独立CSS文件?且因服务端生成模板,无法使用懒加载。


可行解决方案

方法1:新增独立入口拆分样式

直接给Webpack添加多个入口,对应需要拆分的全局样式文件,避免这些样式被主入口引入,从而生成独立的CSS文件。

修改Webpack配置的入口部分:

// 替换或扩展原entry配置
config.entry = {
  main: './src/main.tsx', // 原主入口文件
  other1: './src/styles/pdf-a4.scss', // PDF专属样式
  other2: './src/styles/other-global.scss' // 其他全局样式
}
  • 确保这些新增入口的样式文件不要被主入口import,否则仍会合并到main.css。
  • 在服务端模板中根据场景加载对应CSS:比如渲染PDF页面时引入other1.css,普通页面引入main.css。

方法2:配置splitChunks针对性拆分样式

通过splitChunks的cacheGroups匹配特定路径的样式文件,强制拆分到独立chunk,即使这些样式被主入口引入。

修改optimization配置(建议用webpack-merge合并原有配置,避免覆盖NX默认设置):

const merge = require('webpack-merge');

// ... 原有配置代码

config.optimization = merge(config.optimization || {}, {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      // 可选:拆分第三方库样式到单独文件
      vendorStyles: {
        test: /[\\/]node_modules[\\/].*\.(css|scss)$/,
        name: 'vendor',
        chunks: 'all',
        enforce: true
      },
      // 拆分PDF相关样式
      pdfStyles: {
        test: /styles[\\/]pdf.*\.(scss)$/, // 匹配你的PDF样式文件路径
        name: 'other1',
        chunks: 'all',
        enforce: true // 强制拆分,忽略chunk大小限制
      },
      // 拆分其他全局样式
      otherGlobalStyles: {
        test: /styles[\\/]other.*\.(scss)$/, // 匹配其他样式文件路径
        name: 'other2',
        chunks: 'all',
        enforce: true
      }
    }
  }
});
  • 调整test规则中的路径,确保准确匹配需要拆分的样式文件。
  • enforce: true确保即使chunk体积很小,也会被拆分生成独立CSS。

关键注意事项

  • 保持MiniCssExtractPlugin的filename: '[name].css'配置,确保每个chunk的CSS以chunk名称命名。
  • 非模块化的全局样式不要使用.module.scss后缀,避免被CSS Modules处理(当前配置已区分模块化和非模块化样式,无需修改)。
  • 服务端模板需根据业务场景动态引入对应的CSS文件,比如PDF页面加载other1.css,常规页面加载main.css。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:59:55