Favicon — это небольшая иконка сайта, отображающаяся во вкладке браузера, списке закладок, истории, поисковой выдаче браузера, а также в интерфейсах мобильных устройств. Современные браузеры и платформы используют не один файл, а целый набор иконок разных размеров и форматов.
Типичный набор включает:
Ручное создание такого набора неудобно, особенно при поддержке нескольких окружений и автоматической сборки. В Webpack генерация favicon и связанных файлов обычно автоматизируется через плагины.
При ручном подходе возникают следующие сложности:
Webpack решает эти проблемы через:
HtmlWebpackPlugin;Для генерации favicon в Webpack чаще всего используются:
| Инструмент | Назначение |
|---|---|
favicons-webpack-plugin |
Полная генерация favicon и meta-тегов |
html-webpack-plugin |
Вставка тегов в HTML |
copy-webpack-plugin |
Копирование готовых иконок |
image-minimizer-webpack-plugin |
Оптимизация изображений |
sharp |
Обработка изображений |
svg-to-ico |
Генерация .ico |
Наиболее распространённый вариант — связка:
npm install favicons-webpack-plugin html-webpack-plugin --save-dev
Пример структуры:
project/
├─ src/
│ ├─ assets/
│ │ └─ logo.png
│ ├─ index.js
│ └─ index.html
├─ dist/
├─ webpack.config.js
└─ package.json
Исходное изображение обычно имеет:
Простейшая конфигурация:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new FaviconsWebpackPlugin({
logo: './src/assets/logo.png'
})
]
};
После сборки Webpack:
Плагин может создавать:
dist/
├─ android-chrome-192x192.png
├─ android-chrome-512x512.png
├─ apple-touch-icon.png
├─ favicon.ico
├─ favicon-16x16.png
├─ favicon-32x32.png
├─ mstile-150x150.png
├─ safari-pinned-tab.svg
└─ manifest.webmanifest
Также автоматически добавляются теги:
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#ffffff">
SVG подходит лучше PNG, поскольку:
Пример:
new FaviconsWebpackPlugin({
logo: './src/assets/logo.svg'
})
Преимущества SVG:
По умолчанию иконки генерируются в корне dist.
Изменение пути:
new FaviconsWebpackPlugin({
logo: './src/assets/logo.png',
outputPath: 'assets/favicons'
})
Результат:
dist/
└─ assets/
└─ favicons/
HTML автоматически получит корректные пути.
При деплое в подкаталог важно корректно формировать URL.
Пример:
new FaviconsWebpackPlugin({
logo: './src/assets/logo.png',
publicPath: '/static/'
})
Результат:
<link rel="icon" href="/static/favicon.ico">
Префикс помогает организовать файлы:
new FaviconsWebpackPlugin({
logo: './src/assets/logo.png',
prefix: 'icons/'
})
Файлы будут созданы так:
dist/
└─ icons/
├─ favicon.ico
├─ apple-touch-icon.png
└─ manifest.webmanifest
Явное имя файла:
new FaviconsWebpackPlugin({
logo: './src/assets/logo.png',
favicons: {
icons: {
favicons: true
}
},
filename: 'site-[contenthash].ico'
})
Хеширование помогает избежать проблем с кэшированием.
favicons-webpack-plugin автоматически интегрируется с
HtmlWebpackPlugin.
Конфигурация:
plugins: [
new HtmlWebpackPlugin(),
new FaviconsWebpackPlugin({
logo: './src/logo.png'
})
]
Во время генерации HTML:
link;meta;Плагин поддерживает описание PWA-приложения.
Пример:
new FaviconsWebpackPlugin({
logo: './src/logo.png',
favicons: {
appName: 'My Application',
appDescription: 'Webpack demo app',
developerName: 'Developer',
developerURL: 'https://example.com',
background: '#ffffff',
theme_color: '#222222'
}
})
Эти параметры попадают:
Можно отключать ненужные платформы.
Пример:
new FaviconsWebpackPlugin({
logo: './src/logo.png',
favicons: {
icons: {
android: true,
appleIcon: true,
appleStartup: false,
coast: false,
favicons: true,
firefox: false,
windows: true,
yandex: false
}
}
})
Это уменьшает:
Файл .ico остаётся важным из-за совместимости со старыми
браузерами.
Плагин автоматически создаёт:
favicon.ico
Обычно внутри содержатся размеры:
Иногда нужны разные favicon для:
Пример:
const isProd = process.env.NODE_ENV === 'production';
new FaviconsWebpackPlugin({
logo: isProd
? './src/assets/logo-prod.png'
: './src/assets/logo-dev.png'
})
Генерация иконок может замедлять dev-сборку.
Оптимизация:
const isProd = process.env.NODE_ENV === 'production';
plugins: [
...(isProd
? [
new FaviconsWebpackPlugin({
logo: './src/logo.png'
})
]
: [])
]
Браузеры aggressively кэшируют favicon.
Типичные проблемы:
Решения:
filename: 'favicon-[contenthash].ico'
или:
<link rel="icon" href="/favicon.ico?v=2">
Webpack-хеширование считается лучшим вариантом.
Manifest используется для PWA.
Пример содержимого:
{
"name": "My App",
"short_name": "App",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Webpack может генерировать его автоматически.
new FaviconsWebpackPlugin({
logo: './src/logo.png',
favicons: {
display: 'standalone'
}
})
Возможные значения:
| Значение | Описание |
|---|---|
browser |
Обычный режим |
standalone |
Как отдельное приложение |
fullscreen |
Полноэкранный режим |
minimal-ui |
Минимальный UI |
favicons: {
orientation: 'portrait'
}
Варианты:
Для Android и Windows Tiles:
favicons: {
background: '#121212'
}
Цвет применяется:
Настройка цвета интерфейса браузера:
favicons: {
theme_color: '#0f172a'
}
На мобильных устройствах:
iOS использует специальные иконки:
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
Webpack автоматически генерирует нужные размеры.
Safari поддерживает pinned tabs через SVG.
Генерируется:
safari-pinned-tab.svg
Также добавляется:
<link rel="mask-icon" color="#000000">
Если иконки уже готовы вручную:
npm install copy-webpack-plugin --save-dev
Конфигурация:
const CopyWebpackPlugin = require('copy-webpack-plugin');
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'src/favicons',
to: 'favicons'
}
]
})
]
Иногда favicon подключается без плагинов.
HTML:
<link rel="icon" type="image/png" href="/favicon.png">
Webpack может обработать импорт:
import './assets/favicon.png';
Но такой подход:
Webpack 5 позволяет импортировать favicon как asset.
Пример:
module.exports = {
module: {
rules: [
{
test: /\.(png|ico)$/i,
type: 'asset/resource'
}
]
}
};
Импорт:
import favicon from './favicon.ico';
Иногда требуется полный контроль над изображениями.
Установка:
npm install sharp --save-dev
Пример генерации:
const sharp = require('sharp');
sharp('logo.png')
.resize(32, 32)
.toFile('favicon-32x32.png');
Подобный подход полезен при:
Большое количество PNG увеличивает размер сборки.
Использование минимизации:
npm install image-minimizer-webpack-plugin imagemin --save-dev
Пример:
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
optimization: {
minimizer: [
new ImageMinimizerPlugin({
minimizer: {
implementation:
ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['optipng', { optimizationLevel: 5 }]
]
}
}
})
]
}
В multi-tenant проектах favicon может зависеть от бренда.
Пример:
const brand = process.env.BRAND;
new FaviconsWebpackPlugin({
logo: `./brands/${brand}/logo.png`
})
Это особенно актуально для:
Development:
plugins: []
Production:
plugins: [
new FaviconsWebpackPlugin({
logo: './src/logo.png'
})
]
Обычно используется:
webpack.common.jswebpack.dev.jswebpack.prod.jsНекоторые платформы плохо отображают прозрачные favicon.
Рекомендации:
Практические правила:
| Рекомендация | Причина |
|---|---|
| Простая форма | Маленький размер |
| Минимум текста | Нечитаемость |
| Контрастность | Видимость вкладки |
| Толстые линии | Избежание размытия |
| SVG-источник | Масштабируемость |
| Размер 512x512+ | Качественная генерация |
После сборки необходимо проверять:
Инструмент Lighthouse проверяет:
Ошибки Lighthouse часто связаны:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
module.exports = {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new FaviconsWebpackPlugin({
logo: './src/assets/logo.svg',
cache: true,
favicons: {
appName: 'Webpack App',
appDescription: 'Modern frontend application',
developerName: 'Company',
background: '#ffffff',
theme_color: '#1e293b',
icons: {
android: true,
appleIcon: true,
favicons: true,
windows: true,
yandex: false
}
}
})
]
};