首页 > 解决方案 > 将 Angular 8 迁移到 12:自定义 Webpack 配置无法使用 svg-sprite-loader 构建 SVG 精灵

问题描述

我从以前的开发人员那里继承了这个项目,并使用官方网站的提示将其更新为 12 角度,但是 svg sprite 的组装存在问题。我在下面附上了自定义的 webpack 配置和 package.json,感谢您的帮助!

自定义 webpack.config.js

const webpack = require('webpack');
const path = require('path');
const SvgSpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const url = require('postcss-url');
const urlPlugin = url([{ filter: '**/assets/calltrace/*.svg', url: 'inline' }]);

const sprites = {
    calltrace: path.resolve(__dirname, 'src/assets/calltrace'),
    reports: path.resolve(__dirname, 'src/assets/reports')
};
const keyPrefix = 'INSIGHT_';
const keys = Object.keys(process.env).filter(key => key.startsWith(keyPrefix));
const env = {};

keys.forEach(key => (env[key] = JSON.stringify(process.env[key])));
console.log('env=', env);

/**
 * Entry point for custom webpack configuration.
 *
 * @angular-builders/custom-webpack passes the Angular CLI prepared
 * webpack config, which we can customize here.
 *
 */
function customizeWebpackConfig(config, _) {
    excludeIconsFromModuleRulesForSvg(config);
    addModuleRuleForIconSprite(config);
    addPluginForIconSprite(config);
    addPluginForEnvVariables(config);
    addPostCSSUrlPlugin(config);
    addHTMLPluginForExports(config);

    return config;
}

function excludeIconsFromModuleRulesForSvg(config) {
    config.module.rules.forEach(rule => {
        if (!rule.test.toString().includes('svg')) return;

        excludeIconsFromModuleRule(rule);
    });
}

function excludeIconsFromModuleRule(rule) {
    if (!rule.exclude) {
        rule.exclude = Object.values(sprites);
        return;
    }

    if (rule.exclude instanceof Array) {
        rule.exclude.concat(Object.values(sprites));
        return;
    }

    rule.exclude = [rule.exclude, ...Object.values(sprites)];
}

function addModuleRuleForIconSprite(config) {
    config.module.rules.push({
        test: /\.svg$/,
        loader: 'svg-sprite-loader',
        include: Object.values(sprites),
        options: {
            esModule: false,
            extract: true,
            spriteFilename: `module-sprites.svg`,
            publicPath: '/',
            symbolId: '[name]'
        }
    });
}

function addPluginForIconSprite(config) {
    config.plugins.push(new SvgSpriteLoaderPlugin({ plainSprite: true }));
}

function addPluginForEnvVariables(config) {
    config.plugins.push(new webpack.DefinePlugin({ ENV_VARS: env }));
}

function addPostCSSUrlPlugin(config) {
    for (const rule of config.module.rules) {
        const loader = Array.isArray(rule.use) && rule.use.find(x => x.loader && x.loader.includes('postcss-loader'));
        if (loader) {
            loader.options.plugins = [urlPlugin];
        }
    }
}

function addHTMLPluginForExports(config) {
    config.entry.calltrace = [__dirname + '/src/app/calltrace/calltrace.module.ts'];
    config.plugins.push(
        new HtmlWebpackPlugin({
            filename: 'export.html',
            template: './export-template.ejs',
            inlineSource: '.(js|css)$',
            chunks: ['runtime', 'polyfills', 'styles', 'vendor', 'calltrace', 'main'],
            chunksSortMode: 'manual'
        })
    );

    config.plugins.push(new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin));
}

module.exports = customizeWebpackConfig;

包.json

{
  "name": "insight",
  "version": "0.0.1",
  "scripts": {
    "ng": "ng",
    "start": "concurrently --kill-others --names client,server,ws \"ng serve\" \"nodemon ./server.js\" \"nodemon ./server/ws-server.js\"",
    "start:prod": "npm run build:prod && npm run server",
    "build": "ng build",
    "build:prod": "ng build --configuration production",
    "lint": "ng lint",
    "test": "ng lint && ng test --configuration=test",
    "watch": "ng test --configuration=test --browsers ChromeHeadless --watch --reporters dots",
    "e2e": "ng e2e",
    "ci": "npm run format:test && ng lint && ng test --configuration=test --browsers ChromeTravisCi --code-coverage && npm run e2e && npm run build:prod",
    "format:write": "prettier **/*.{ts,json,md,scss} --write",
    "format:test": "prettier **/*.{ts,json,md,scss} --list-different",
    "release": "standard-version && git push --follow-tags origin master",
    "analyze": "npm run build:prod -- --stats-json && webpack-bundle-analyzer ./dist/stats.json",
    "server": "concurrently --kill-others --names server,websocket-server \"nodemon ./server.js\" \"nodemon ./server/ws-server.js\"",
    "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
    "custom": "cross-env INSIGHT_SERVER=http://bigdata-mediation07.msk.mts.ru INSIGHT_WS=ws://bigdata-mediation07.msk.mts.ru npm run build:prod"
  },
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "commitlint": {
    "extends": [
      "@commitlint/config-conventional"
    ]
  },
  "private": true,
  "dependencies": {
    "@amcharts/amcharts4": "^4.10.22",
    "@amcharts/amcharts4-geodata": "^4.1.22",
    "@angular-extensions/elements": "^12.6.0",
    "@angular/animations": "~12.2.0",
    "@angular/cdk": "^12.2.8",
    "@angular/cdk-experimental": "^12.2.8",
    "@angular/common": "~12.2.0",
    "@angular/compiler": "~12.2.0",
    "@angular/core": "~12.2.0",
    "@angular/elements": "^12.2.7",
    "@angular/flex-layout": "^9.0.0-beta.31",
    "@angular/forms": "~12.2.0",
    "@angular/localize": "^12.2.7",
    "@angular/material": "^12.2.8",
    "@angular/material-moment-adapter": "^12.2.8",
    "@angular/platform-browser": "~12.2.0",
    "@angular/platform-browser-dynamic": "~12.2.0",
    "@angular/router": "~12.2.0",
    "@mts/daterangepicker": "^9.5.0",
    "@mts/login": "^0.2.3",
    "@ngrx/effects": "^12.4.0",
    "@ngrx/entity": "^12.4.0",
    "@ngrx/router-store": "^12.4.0",
    "@ngrx/schematics": "^12.4.0",
    "@ngrx/store": "^12.4.0",
    "@ngrx/store-devtools": "^12.4.0",
    "@ngx-translate/core": "^13.0.0",
    "@ngx-translate/http-loader": "^6.0.0",
    "angular-split": "^5.0.0",
    "bmt": "^0.1.1",
    "bootstrap": "^5.1.1",
    "browser-detect": "^0.2.28",
    "date-fns": "^2.25.0",
    "date-fns-tz": "^1.1.6",
    "document-register-element": "^1.14.10",
    "fast-memoize": "^2.5.2",
    "file-saver": "^2.0.5",
    "fontfaceobserver": "^2.1.0",
    "jasmine-marbles": "^0.8.4",
    "lodash": "^4.17.21",
    "mark.js": "^8.11.1",
    "moment": "^2.29.1",
    "moment-duration-format": "^2.3.2",
    "moment-timezone": "^0.5.33",
    "ng-click-outside": "^9.0.0",
    "ngx-bootstrap": "^7.1.0",
    "ngx-mask": "^12.0.0",
    "ngx-perfect-scrollbar": "^10.1.1",
    "ngx-scrollbar": "^8.0.0",
    "resize-observer-polyfill": "^1.5.1",
    "rxjs": "~6.6.0",
    "rxjs-tslint-rules": "^4.34.8",
    "tslib": "^2.3.0",
    "xhr-mock": "^2.5.1",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-builders/custom-webpack": "^12.1.2",
    "@angular-devkit/build-angular": "~12.2.7",
    "@angular/cli": "~12.2.7",
    "@angular/compiler-cli": "~12.2.0",
    "@angular/language-service": "~12.2.7",
    "@commitlint/cli": "^13.2.0",
    "@commitlint/config-conventional": "^13.2.0",
    "@mts/tslint": "^2.0.2",
    "@types/express": "^4.17.13",
    "@types/jasmine": "~3.8.0",
    "@types/jasminewd2": "^2.0.10",
    "@types/lodash": "^4.14.175",
    "@types/moment-duration-format": "^2.2.3",
    "@types/node": "^12.11.1",
    "@types/uuid": "^8.3.1",
    "@types/ws": "^8.2.0",
    "body-parser": "^1.19.0",
    "codelyzer": "^6.0.0",
    "compression": "^1.7.4",
    "concurrently": "^6.3.0",
    "cors": "^2.8.5",
    "cross-env": "^7.0.3",
    "express": "^4.16.4",
    "html-webpack-inline-source-plugin": "1.0.0-beta.2",
    "html-webpack-plugin": "^5.3.2",
    "http-proxy-middleware": "^2.0.1",
    "husky": "^7.0.2",
    "jasmine-core": "^3.9.0",
    "jasmine-spec-reporter": "^7.0.0",
    "karma": "~6.3.4",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.7.0",
    "karma-spec-reporter": "0.0.32",
    "nocache": "^3.0.1",
    "nodemon": "^2.0.13",
    "npm-check": "^5.9.2",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.3.9",
    "postcss-loader": "^6.1.1",
    "postcss-url": "^10.1.3",
    "prettier": "^2.4.1",
    "pretty-quick": "^3.1.1",
    "protractor": "~7.0.0",
    "rimraf": "^3.0.2",
    "sass-migrator": "^1.5.2",
    "standard-version": "^9.3.1",
    "svg-sprite-loader": "^6.0.9",
    "ts-node": "^10.2.1",
    "tslint": "^6.1.3",
    "typescript": "~4.3.5",
    "url": "^0.11.0",
    "webpack-bundle-analyzer": "^4.4.2",
    "ws": "^8.2.3"
  }
}

标签: angularwebpackangular12webpack-5svg-sprite-loader

解决方案


推荐阅读