SVG-файлы в Webpack могут обрабатываться несколькими способами. Один и тот же файл способен выступать:
Способ обработки зависит от архитектуры проекта, требований к производительности и используемого UI-фреймворка.
Webpack 5 значительно упростил работу с SVG благодаря Asset Modules, однако SVG остаётся особым типом ресурса, поскольку сочетает свойства изображения и XML-документа.
Наиболее простой вариант — обработка SVG как файла, который копируется в итоговую сборку.
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/resource'
}
]
}
};
import logo from './images/logo.svg';
console.log(logo);
После сборки импорт будет содержать URL:
/assets/logo.a1b2c3.svg
const image = document.createElement('img');
image.src = logo;
document.body.append(image);
Webpack:
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/resource',
generator: {
filename: 'images/[name].[contenthash][ext]'
}
}
]
}
};
dist/
└── images/
└── logo.6fd9e1.svg
| Шаблон | Назначение |
|---|---|
[name] |
имя файла |
[ext] |
расширение |
[hash] |
общий hash |
[contenthash] |
hash содержимого |
[path] |
исходный путь |
SVG может внедряться прямо в JavaScript или CSS.
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/inline'
}
]
}
};
import icon from './icon.svg';
console.log(icon);
Результат:
data:image/svg+xml;base64,...
Data URI подходит для:
Webpack позволяет автоматически определять режим обработки по размеру файла.
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024
}
}
}
]
}
};
Это оптимальный компромисс для большинства проектов.
.logo {
background-image: url('./logo.svg');
}
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
test: /\.svg$/i,
type: 'asset/resource'
}
]
}
};
Webpack:
Иногда требуется получить XML-содержимое файла.
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/source'
}
]
}
};
import svgContent from './icon.svg';
console.log(svgContent);
Результат:
<svg xmlns="http://www.w3.org/2000/svg">
Подход полезен при:
Один из наиболее популярных сценариев — преобразование SVG в React-компонент.
Для этого обычно используется SVGR.
npm install @svgr/webpack --save-dev
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
}
]
}
};
import Logo from './logo.svg';
export default function App() {
return <Logo />;
}
SVG превращается в полноценный React-компонент.
Исходный SVG:
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" />
</svg>
Преобразуется примерно в:
const SvgComponent = (props) => (
<svg width="100" height="100" {...props}>
<circle cx="50" cy="50" r="40" />
</svg>
);
export default SvgComponent;
<Logo width={200} height={200} />
<Logo className="icon" />
<Logo fill="red" />
SVG становится частью компонентной системы.
Наиболее удобная практика — использование currentColor.
<svg fill="currentColor">
<Logo style={{ color: 'tomato' }} />
SVG автоматически наследует цвет текста.
Часто требуется:
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
oneOf: [
{
resourceQuery: /url/,
type: 'asset/resource'
},
{
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
}
]
}
]
}
};
import Logo from './logo.svg';
import logoUrl from './logo.svg?url';
SVGR может автоматически оптимизировать SVG.
npm install svgo --save-dev
{
test: /\.svg$/i,
use: [
{
loader: '@svgr/webpack',
options: {
svgo: true
}
}
]
}
SVGO:
Некоторые SVG могут ломаться после оптимизации.
Особенно часто проблемы возникают с:
Очень важная настройка:
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
}
]
}
}
Без viewBox SVG теряет адаптивность.
SVG-спрайт — единый SVG-файл, содержащий множество иконок.
Спрайт содержит набор символов:
<symbol id="search">
<path d="..." />
</symbol>
<symbol id="close">
<path d="..." />
</symbol>
Использование:
<svg>
<use href="#search"></use>
</svg>
Наиболее популярный инструмент — svg-sprite-loader.
npm install svg-sprite-loader --save-dev
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
include: /icons/,
use: [
{
loader: 'svg-sprite-loader',
options: {
symbolId: '[name]'
}
}
]
}
]
}
};
import './icons/search.svg';
import './icons/close.svg';
<svg class="icon">
<use href="#search"></use>
</svg>
Настройка определяет идентификатор иконки.
symbolId: 'icon-[name]'
Результат:
<use href="#icon-search"></use>
На практике SVG обычно делят на категории.
src/icons/
Используются как спрайт.
src/images/
Используются как asset/resource.
src/components/icons/
Используются через SVGR.
SVG может проходить через несколько loader одновременно.
{
test: /\.svg$/i,
use: [
'@svgr/webpack',
'url-loader'
]
}
Webpack выполняет loaders справа налево.
url-loader → @svgr/webpack
Это критически важно при сложной обработке SVG.
Часто SVG должны обрабатываться по-разному в зависимости от источника импорта.
{
test: /\.svg$/i,
issuer: /\.css$/,
type: 'asset/resource'
}
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
}
Вместо множества независимых rules лучше использовать oneOf.
{
test: /\.svg$/i,
oneOf: [
{
resourceQuery: /component/,
use: ['@svgr/webpack']
},
{
resourceQuery: /inline/,
type: 'asset/inline'
},
{
type: 'asset/resource'
}
]
}
import Icon from './icon.svg?component';
import dataUrl from './icon.svg?inline';
import iconUrl from './icon.svg';
Подходит для:
Подходит для:
Подходит для:
SVG является XML-документом и способен содержать:
<script>
alert(1)
</script>
При загрузке SVG от пользователей необходимо:
SVG-компоненты могут участвовать в tree shaking.
import SearchIcon from './search.svg';
Неиспользуемые SVG не попадут в bundle при корректной конфигурации.
Крупные SVG можно загружать динамически.
const illustration = await import('./hero.svg');
Webpack способен выносить SVG в отдельные chunks.
Особенно полезно для:
SVG обычно не требуют sourcemap, однако при inline SVG внутри JS размер карт может заметно увеличиваться.
Особенно это касается:
icons/ → sprite или svgr
images/ → asset/resource
ui-icons/ → svgr
illustrations → resource
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
oneOf: [
{
include: /icons/,
use: ['@svgr/webpack']
},
{
type: 'asset/resource'
}
]
}
]
}
};
| Подход | Назначение |
|---|---|
| asset/resource | отдельный файл |
| asset/inline | Data URI |
| asset/source | строковое содержимое |
| SVGR | React-компонент |
| svg-sprite-loader | SVG-спрайт |
| asset | автоматический режим |
| Способ | HTTP-запрос | Управление стилями | Кеширование |
|---|---|---|---|
| resource | да | ограничено | хорошее |
| inline | нет | ограничено | отсутствует |
| component | нет | максимальное | внутри JS |
| sprite | один запрос | хорошее | отличное |