Webpack5配置问题:为Joomla4模板生成CSS压缩与未压缩资源失败
问题描述
我要给自定义Joomla4模板生成压缩和未压缩的资源,写了如下webpack.config.js配置:
const path = require('path'); const CopyPlugin = require('copy-webpack-plugin'); const TerserPlugin = require("terser-webpack-plugin"); const dist_path = path.resolve(__dirname, 'dist'); const media_root_path = 'media/templates/site/tofu'; module.exports = { devtool: 'source-map', entry: { 'js/template.js': './src/js/template.js', 'css/template.css': './src/scss/template.scss', 'js/template.min.js': './src/js/template.js', 'css/template.min.css': './src/scss/template.scss', }, output: { path: dist_path, filename: "media/templates/site/tofu/[name]", }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [], }, { test: /\.scss$/, exclude: /node_modules/, type: 'asset/resource', generator: { filename: media_root_path + '/css/template.min.css', }, use: [ // Compiles Sass to CSS { loader: 'sass-loader', options: { sourceMap: true, // Prefer `dart-sass` implementation: require('sass'), }, }, ], }, ], }, plugins: [ new CopyPlugin({ patterns: [ { from: path.resolve(__dirname, 'src/index.php'), to: path.resolve(dist_path, 'index.php') }, { from: path.resolve(__dirname, 'src/joomla.asset.json'), to: path.resolve(dist_path, 'joomla.asset.json') }, { from: path.resolve(__dirname, 'src/templateDetails.xml'), to: path.resolve(dist_path, 'templateDetails.xml') }, { from: path.resolve(__dirname, 'src/images'), to: path.resolve(dist_path, media_root_path, 'images') }, ], }) ], optimization: { minimize: true, minimizer: [new TerserPlugin({ include: /\.min\.js$/ })], }, stats: { loggingDebug: ['sass-loader'] }, };
JavaScript部分正常,但SCSS转CSS并生成*.css、*.min.css和对应的source map时出问题:
- 原配置报错:
[webpack-cli] Error: Conflict: Multiple chunks emit assets to the same filename media/templates/site/tofu/css/template.min.css (chunks 61 and 301) at /Users/denis/Projects/tofu/node_modules/webpack/lib/Compilation.js:4625:12 at /Users/denis/Projects/tofu/node_modules/webpack/lib/Cache.js:93:5 at Hook.eval [as callAsync] (eval at create (/Users/denis/Projects/tofu/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1) at Cache.get (/Users/denis/Projects/tofu/node_modules/webpack/lib/Cache.js:75:18) at ItemCacheFacade.get (/Users/denis/Projects/tofu/node_modules/webpack/lib/CacheFacade.js:115:15) at /Users/denis/Projects/tofu/node_modules/webpack/lib/Compilation.js:4571:22 at arrayEach (/Users/denis/Projects/tofu/node_modules/neo-async/async.js:2405:9) at Object.each (/Users/denis/Projects/tofu/node_modules/neo-async/async.js:2846:9) at /Users/denis/Projects/tofu/node_modules/webpack/lib/Compilation.js:4560:14 at symbolIterator (/Users/denis/Projects/tofu/node_modules/neo-async/async.js:3482:9)
- 注释掉入口项
'css/template.min.css': './src/scss/template.scss',后,Webpack能构建,但生成的template.css是无效的Webpack引导代码:
/******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 797: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { module.exports = __webpack_require__.p + "media/templates/site/tofu/css/template.min.css"; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/global */ /******/ (() => { /******/ __webpack_require__.g = (function() { /******/ if (typeof globalThis === 'object') return globalThis; /******/ try { /******/ return this || new Function('return this')(); /******/ } catch (e) { /******/ if (typeof window === 'object') return window; /******/ } /******/ })(); /******/ })(); /******/ /******/ /* webpack/runtime/publicPath */ /******/ (() => { /******/ var scriptUrl; /******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + ""; /******/ var document = __webpack_require__.g.document; /******/ if (!scriptUrl && document) { /******/ if (document.currentScript) /******/ scriptUrl = document.currentScript.src; /******/ if (!scriptUrl) { /******/ var scripts = document.getElementsByTagName("script"); /******/ if(scripts.length) { /******/ var i = scripts.length - 1; /******/ while (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src; /******/ } /******/ } /******/ } /******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration /******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic. /******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser"); /******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/"); /******/ __webpack_require__.p = scriptUrl + "../../../../../"; /******/ })(); /******/ /************************************************************************/ /******/ /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module doesn't tell about it's top-level declarations so it can't be inlined /******/ var __webpack_exports__ = __webpack_require__(797); /******/ /******/ })() ; /*# sourceMappingURL=template.css.map*/
请问配置哪里错了?
问题原因与修复方案
核心错误点
- SCSS规则硬编码输出文件名:SCSS处理规则里的
generator.filename固定写死了template.min.css,所有SCSS入口都会输出到这个路径,导致两个SCSS入口冲突。 - 未正确处理SCSS入口:Webpack默认把入口当JS模块处理,你直接将SCSS写入
entry却没配置完整的loader链(缺css-loader和提取CSS的插件),导致Webpack生成了引用CSS资源的JS模块,而非真正的CSS文件。 - 缺少CSS压缩配置:仅配置了JS的Terser压缩,没有处理CSS压缩,无法生成真正的
.min.css。
修复后的配置
需要引入mini-css-extract-plugin提取CSS,css-minimizer-webpack-plugin压缩CSS,调整入口和规则如下:
const path = require('path'); const CopyPlugin = require('copy-webpack-plugin'); const TerserPlugin = require("terser-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const dist_path = path.resolve(__dirname, 'dist'); const media_root_path = 'media/templates/site/tofu'; module.exports = { devtool: 'source-map', entry: { // JS入口:只保留一个,通过优化生成压缩版 'js/template': './src/js/template.js', // SCSS入口:只保留一个,通过优化生成压缩版 'css/template': './src/scss/template.scss', }, output: { path: dist_path, filename: `${media_root_path}/[name].js`, // 每次构建清理dist目录 clean: true }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, // 可选:添加babel转译JS,兼容旧浏览器 use: ['babel-loader'] }, { test: /\.scss$/, exclude: /node_modules/, use: [ // 将CSS提取为独立文件 MiniCssExtractPlugin.loader, // 解析CSS中的@import和url() 'css-loader', // 编译SCSS到CSS { loader: 'sass-loader', options: { sourceMap: true, implementation: require('sass'), }, }, ], }, ], }, plugins: [ new CopyPlugin({ patterns: [ { from: './src/index.php', to: `${dist_path}/index.php` }, { from: './src/joomla.asset.json', to: `${dist_path}/joomla.asset.json` }, { from: './src/templateDetails.xml', to: `${dist_path}/templateDetails.xml` }, { from: './src/images', to: `${dist_path}/${media_root_path}/images` }, ], }), new MiniCssExtractPlugin({ // 生成未压缩的CSS文件 filename: `${media_root_path}/[name].css`, }), ], optimization: { minimize: true, minimizer: [ // 为未压缩JS生成.min.js new TerserPlugin({ include: /\.js$/, exclude: /\.min\.js$/, filename: `[name].min.js`, }), // 为未压缩CSS生成.min.css new CssMinimizerPlugin({ include: /\.css$/, exclude: /\.min\.css$/, filename: `[name].min.css`, }), ], }, };
关键调整说明
- 合并重复入口:只保留一个JS和一个SCSS入口,通过压缩插件自动生成对应的
.min版本,避免文件名冲突。 - 完整SCSS loader链:用
MiniCssExtractPlugin.loader提取CSS,css-loader解析CSS语法,sass-loader编译SCSS,最终生成真正的CSS文件。 - 自动生成压缩文件:通过
TerserPlugin和CssMinimizerPlugin分别处理JS和CSS的压缩,无需手动配置多个入口。
安装依赖
执行以下命令安装新增的依赖:
npm install mini-css-extract-plugin css-minimizer-webpack-plugin css-loader babel-loader @babel/core @babel/preset-env --save-dev
内容的提问来源于stack exchange,提问作者Denis Steinman
相关产品推荐
相关产品推荐

