assetModuleFilename и именование ресурсов

В Webpack система работы с файлами-ассетами построена вокруг концепции Asset Modules, которая заменяет необходимость в file-loader, url-loader и частично raw-loader. Центральным элементом управления тем, как будут называться и куда будут помещаться файлы, выступает output.assetModuleFilename и локальные переопределения через generator.filename.


Базовая роль assetModuleFilename

Ключевая настройка:

output: {
  assetModuleFilename: 'assets/[hash][ext][query]'
}

Она задаёт дефолтный шаблон имени для всех ассетов, которые обрабатываются через Asset Modules и не имеют локального переопределения.

Поведение по умолчанию

Если в конфигурации указано:

output: {
  assetModuleFilename: 'static/media/[hash][ext]'
}

то любые файлы, попадающие под тип:

  • asset/resource
  • asset/inline
  • asset (автовыбор)

будут сохраняться в указанную директорию с именем, основанным на хеше.


Плейсхолдеры в именовании

Webpack поддерживает набор специальных токенов, которые подставляются в процессе сборки.

[hash]

Глобальный хеш сборки или ассета (в зависимости от контекста использования).

images/8f3a1c9d8c.png

Используется для:

  • кэш-бастинга
  • предотвращения конфликтов имён

[contenthash]

Более стабильный вариант, основанный на содержимом конкретного файла.

Чаще применяется через generator.filename, но также может использоваться в общих шаблонах.


[name]

Исходное имя файла без расширения:

logo.png → logo

Пример:

assetModuleFilename: 'assets/[name].[hash][ext]'

[ext]

Расширение файла, включая точку:

.png, .jpg, .svg

Важно: Webpack сохраняет оригинальное расширение, если оно не переопределено loader-логикой.


[path]

Относительный путь исходного файла от context.

Используется для сохранения структуры проекта:

assetModuleFilename: 'assets/[path][name][hash][ext]'

Если файл находится в:

src/images/icons/logo.svg

результат может выглядеть как:

assets/images/icons/logoa1b2c3.svg

[query]

Сохраняет query-параметры, если они присутствуют в запросе импорта:

import img from './logo.png?inline'

Разделение глобальной и локальной конфигурации

output.assetModuleFilename

Работает как глобальный fallback для всех ассетов.

Rule.generator.filename

Позволяет переопределить шаблон на уровне конкретного правила:

module: {
  rules: [
    {
      test: /\.png/,
      type: 'asset/resource',
      generator: {
        filename: 'images/[name][contenthash][ext]'
      }
    }
  ]
}

Локальная настройка всегда имеет приоритет над глобальной.


Разделение типов Asset Modules и влияние на именование

asset/resource

Файлы сохраняются на диск.

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|svg)$/,
        type: 'asset/resource'
      }
    ]
  }
}

Результат:

dist/assets/8c1f3a.png

asset/inline

Файлы не сохраняются, а преобразуются в base64 URI.

В этом случае assetModuleFilename не применяется, так как физического файла нет.


asset (автовыбор)

Webpack автоматически решает:

  • маленькие файлы → inline
  • большие файлы → resource

Порог задаётся через:

parser: {
  dataUrlCondition: {
    maxSize: 4 * 1024
  }
}

Приоритеты конфигурации именования

Иерархия применения шаблонов:

  1. generator.filename (Rule-level)
  2. output.assetModuleFilename (global fallback)
  3. внутренние дефолты Webpack

Практика построения структуры ассетов

Единая директория

output: {
  assetModuleFilename: 'assets/[hash][ext]'
}

Результат:

assets/a1b2c3d4.png
assets/f9e8d7c6.svg

Плюс: простота Минус: отсутствие структуры


Разделение по типам файлов

module: {
  rules: [
    {
      test: /\.png$/,
      type: 'asset/resource',
      generator: {
        filename: 'images/[contenthash][ext]'
      }
    },
    {
      test: /\.woff2$/,
      type: 'asset/resource',
      generator: {
        filename: 'fonts/[contenthash][ext]'
      }
    }
  ]
}

Структура:

images/1a2b3c.png
fonts/9f8e7d.woff2

Сохранение структуры исходников

output: {
  assetModuleFilename: '[path][name][contenthash][ext]'
}

Результат:

src/images/icons/logo.8f3a.png

или в dist:

dist/src/images/icons/logo.8f3a.png

Такой подход сохраняет читаемость, но может засорять output.


Кэширование и стабильность имён

Использование hash и contenthash напрямую влияет на стратегию кеширования браузера.

Сценарий с contenthash

assetModuleFilename: 'assets/[name].[contenthash][ext]'

При изменении изображения:

  • изменяется только хеш
  • имя файла меняется
  • браузер загружает новую версию
  • старые файлы могут оставаться в кеше

Связь с publicPath

assetModuleFilename определяет имя файла, но не путь доступа к нему.

output: {
  publicPath: '/static/',
  assetModuleFilename: 'images/[hash][ext]'
}

Результат в runtime:

/static/images/8c1f3a.png

Влияние mode и target на именование

Хотя mode не меняет шаблоны напрямую, он влияет на:

  • агрессивность хеширования
  • оптимизации модулей
  • стабильность идентификаторов

target: 'web' чаще предполагает строгую стратегию кеширования через contenthash.


Распространённые ошибки в именовании ассетов

Использование только [hash]

assetModuleFilename: '[hash][ext]'

Проблема:

  • хеш может меняться даже при неизменном контенте
  • хуже кеширование

Потеря структуры проекта

assetModuleFilename: 'assets/[hash][ext]'

Результат:

  • все файлы в одной папке
  • сложнее дебажить и ориентироваться

Дублирование имен без хеша

assetModuleFilename: '[name][ext]'

Проблема:

  • конфликты файлов с одинаковыми именами
  • перезапись ассетов при сборке

Связь с другими механизмами Webpack

MiniCssExtractPlugin

Хотя относится к CSS, часто используется вместе с asset naming:

new MiniCssExtractPlugin({
  filename: '[name].[contenthash].css'
})

Логика именования становится единообразной между CSS и assets.


SplitChunks и ассеты

Хотя splitChunks не управляет файлами напрямую, он влияет на:

  • структуру бандлов
  • необходимость синхронного кеширования с assetModuleFilename

Рекомендованные паттерны именования

Универсальный продакшн-подход

output: {
  assetModuleFilename: 'assets/[name].[contenthash][ext]'
}

Баланс между:

  • читаемостью
  • кешированием
  • структурой

Строгая сегрегация по типам

generator: {
  filename: 'media/images/[contenthash][ext]'
}
generator: {
  filename: 'media/fonts/[contenthash][ext]'
}

Используется в крупных приложениях с большим количеством ассетов.


Debug-подход

assetModuleFilename: '[path][name][ext]'

Используется в разработке:

  • удобно отслеживать источник файла
  • нет хешей для упрощения анализа