Angular 11升级到13后,LESS文件引用图片无法打包到dist目录
Angular13升级后自定义Webpack处理LESS背景图片路径异常问题
问题说明
使用angular-builders/custom-webpack自定义Webpack配置编译LESS到CSS,Angular11中LESS引用的图片能正常复制到dist目录;升级到Angular13后功能失效:背景图片URL变为本地绝对路径,且图片未被打包到dist目录。
自定义Webpack配置文件
const path = require('path'); const SpritesmithPlugin = require('webpack-spritesmith'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CopyPlugin = require('copy-webpack-plugin'); const TIMESTAMP = Date.now(); module.exports = { entry: { courses: [path.resolve(__dirname, '../assets/less/courses.less')], styles: [path.resolve(__dirname, '../assets/less/styles.less')] }, plugins: [ new SpritesmithPlugin({ src: { cwd: path.resolve(__dirname, '../assets/img/sprite_source'), glob: '*.{png,gif}' }, target: { image: path.resolve(__dirname, '../assets/img/sprite.' + TIMESTAMP + '.png'), css: path.resolve(__dirname, '../assets/less/base/_sprite-source.less') }, apiOptions: { cssImageRef: '../img/sprite.' + TIMESTAMP + '.png' } }), new MiniCssExtractPlugin({ filename: '[name].css' }), new CopyPlugin({ patterns: [ { from: path.resolve(__dirname, "../assets/js/lib/conditional/site.js"), to: path.resolve(__dirname, "../assets/dist/js") }, ], }), ], module: { rules: [ { test: /\.less$/, exclude: [ path.resolve(__dirname, 'src'), ], use: [ MiniCssExtractPlugin.loader, 'css-loader', 'less-loader', ], }, ] }, resolve: { modules: ["node_modules", "spritesmith-generated"], alias: { Assets: path.resolve(__dirname, '../assets') } }, };
angular.json配置
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "xpl": { "root": "", "sourceRoot": "src", "projectType": "application", "architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./webpack.config.js", "replaceDuplicatePlugins": false }, "outputPath": "../assets/dist/ng-new", "index": "src/index.html", "main": "src/main.ts", "tsConfig": "src/tsconfig.app.json", "polyfills": "src/polyfills.ts", "stylePreprocessorOptions": { "includePaths": [ "src/styles" ] }, "assets": [ "src/styles/svgs" ], "styles": [ "src/styles.less", "./custom-bootstrap.scss" ], "scripts": [] }, "configurations": { "production": { "optimization": true, "outputHashing": "none", "sourceMap": false, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ] }, "development": { "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } } }, "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { "browserTarget": "xpl:build", "customWebpackConfig": { "path": "./webpack.config.js", "replaceDuplicatePlugins": true } }, "configurations": { "production": { "browserTarget": "xpl:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "xpl:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "karmaConfig": "./karma.conf.js", "polyfills": "src/polyfills.ts", "tsConfig": "src/tsconfig.spec.json", "scripts": [], "styles": [ "src/styles.less", "./custom-bootstrap.scss" ], "assets": [] } } } }, "xpl-e2e": { "root": "e2e", "sourceRoot": "e2e", "projectType": "application", "architect": { "e2e": { "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "./protractor.conf.js", "devServerTarget": "xpl:serve" } } } } }, "defaultProject": "xpl", "schematics": { "@schematics/angular:component": { "prefix": "xpl", "style": "less" }, "@schematics/angular:directive": { "prefix": "xpl" } } }
编译结果对比
Angular11生成的styles.css片段
.icon-export-white { background-image: url(sprite.1660595463966.png); background-position: -83px -588px; width: 16px; height: 20px; }
Angular13生成的styles.css片段
.icon-export-white { background-image: url(file:///Users/bkoo/Documents/workspace/xplore/xplore-web/src/main/webapp/assets/img/sprite.1660598549301.png); background-position: -83px -588px; width: 16px; height: 20px }
目录结构对比
Angular13的dist目录

Angular11的dist目录

解决办法
1. 修复css-loader的资源处理配置
Angular13适配的Webpack 5中,css-loader默认不再自动处理背景图等资源,需显式开启并添加资源规则:
修改自定义Webpack的module.rules:
module: { rules: [ { test: /\.less$/, exclude: [path.resolve(__dirname, 'src')], use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: true, // 强制开启URL解析 import: true } }, 'less-loader' ] }, // 添加图片资源打包规则 { test: /\.(png|gif)$/, type: 'asset/resource', generator: { filename: 'img/[name].[contenthash][ext]' // 输出到dist/img下 } } ] },
2. 调整SpritesmithPlugin的输出路径
把雪碧图直接生成到dist目录,避免中间目录的路径问题:
new SpritesmithPlugin({ src: { cwd: path.resolve(__dirname, '../assets/img/sprite_source'), glob: '*.{png,gif}' }, target: { image: path.resolve(__dirname, '../assets/dist/ng-new/img/sprite.' + TIMESTAMP + '.png'), css: path.resolve(__dirname, '../assets/less/base/_sprite-source.less') }, apiOptions: { cssImageRef: './img/sprite.' + TIMESTAMP + '.png' // 相对于CSS文件的路径 } }),
3. 补充angular.json的assets配置
确保原始图片资源能被Angular CLI打包:
"assets": [ "src/styles/svgs", { "glob": "**/*", "input": "./assets/img", "output": "./img" } ]
4. 设置MiniCssExtractPlugin的publicPath
如果CSS和图片不在同级目录,需指定publicPath:
new MiniCssExtractPlugin({ filename: '[name].css', publicPath: './' // 根据实际目录结构调整,确保能正确找到图片 })
内容的提问来源于stack exchange,提问作者BKoo
相关产品推荐
相关产品推荐

