Настройка порога для inline через parser.dataUrlCondition

В Webpack 5 механизм Asset Modules заменил многие возможности file-loader, url-loader и raw-loader. Один из наиболее важных режимов — asset, автоматически выбирающий между двумя вариантами обработки:

  • преобразование файла в Data URL (asset/inline);
  • генерация отдельного файла (asset/resource).

Решение принимается на основании размера ресурса. Именно для настройки этого поведения используется параметр parser.dataUrlCondition.

По умолчанию Webpack встраивает небольшие файлы в bundle как Base64-строку, а крупные сохраняет как отдельные файлы в каталоге сборки.


Автоматический режим asset

Тип asset работает как интеллектуальный гибрид:

{
  test: /\.(png|jpg|svg)$/i,
  type: 'asset'
}

При такой конфигурации Webpack:

  • анализирует размер ресурса;

  • сравнивает его с лимитом;

  • принимает решение:

    • встроить файл;
    • либо создать отдельный файл.

Что такое Data URL

Data URL — строковое представление бинарного файла.

Пример:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

Такой контент может использоваться:

  • внутри JavaScript;
  • в CSS;
  • в HTML;
  • в inline-стилях.

После встраивания браузеру не требуется отдельный HTTP-запрос для получения файла.


Проблема чрезмерного inline

Встраивание ресурсов полезно только для небольших файлов.

Если inline применяется к крупным изображениям:

  • bundle резко увеличивается;
  • возрастает объём JavaScript;
  • ухудшается кеширование;
  • браузер дольше парсит код;
  • initial load становится тяжелее.

Поэтому Webpack использует ограничение размера.


Базовая настройка dataUrlCondition

Настройка производится внутри parser.

Пример:

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024
          }
        }
      }
    ]
  }
};

Здесь:

maxSize: 8 * 1024

означает:

8192 байта = 8 KB

Как работает maxSize

Webpack сравнивает размер файла с указанным лимитом.

Если файл меньше:

размер <= maxSize

используется:

asset/inline

Если файл больше:

asset/resource

Пример поведения

Конфигурация

{
  test: /\.svg$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 4 * 1024
    }
  }
}

Файлы

Файл Размер Результат
icon.svg 2 KB inline
logo.svg 3 KB inline
illustration.svg 12 KB resource

Что происходит при inline

Импорт:

import icon from './icon.svg';

После сборки:

const icon =
  "data:image/svg+xml;base64,PHN2ZyB4bWxucz0...";

Что происходит при resource

Импорт:

import image from './photo.png';

После сборки:

const image = "/assets/photo.a1b2c3.png";

Файл физически копируется в output-директорию.


Почему порог важен

Размер inline-ресурсов напрямую влияет на производительность.

Слишком маленький лимит

Проблемы:

  • слишком много HTTP-запросов;
  • дополнительные network round-trip;
  • больше накладных расходов на соединения.

Слишком большой лимит

Проблемы:

  • огромный bundle;
  • рост потребления памяти;
  • ухудшение кеширования;
  • медленный startup.

Типичные значения maxSize

4 KB

Подходит для:

  • SVG-иконок;
  • маленьких PNG;
  • миниатюр.
maxSize: 4 * 1024

8 KB

Наиболее популярный вариант.

maxSize: 8 * 1024

Используется во многих production-конфигурациях.


16 KB

Подходит для:

  • SPA;
  • проектов с HTTP/2;
  • приложений с агрессивным code splitting.
maxSize: 16 * 1024

Настройка для SVG

SVG хорошо сжимается и часто имеет маленький размер.

Поэтому для SVG обычно используют более высокий лимит.

{
  test: /\.svg$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 12 * 1024
    }
  }
}

Настройка для изображений

PNG и JPG быстро увеличиваются в размере.

Для них обычно применяют меньший порог.

{
  test: /\.(png|jpg|jpeg)$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 4 * 1024
    }
  }
}

Раздельные правила для разных форматов

Частая production-практика:

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024
          }
        }
      },

      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024
          }
        }
      }
    ]
  }
};

Inline и Base64 overhead

Base64 увеличивает размер данных примерно на:

~33%

Например:

Исходный файл После Base64
3 KB ~4 KB
6 KB ~8 KB
12 KB ~16 KB

Поэтому чрезмерный inline может существенно увеличить bundle.


Влияние на кеширование

asset/resource

Браузер кеширует файл отдельно:

logo.png

Если JavaScript изменился, изображение может остаться в кеше.


asset/inline

Изображение становится частью JS-файла.

При изменении bundle:

main.js

браузер заново скачивает и JavaScript, и встроенные ресурсы.


Влияние на initial loading

Inline уменьшает количество запросов:

HTML → JS → всё внутри

Это полезно для:

  • иконок;
  • маленьких background-image;
  • preload-контента.

Но крупный inline:

  • увеличивает время загрузки JS;
  • замедляет parse/eval;
  • ухудшает Time To Interactive.

Пример production-конфигурации

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024
          }
        }
      },

      {
        test: /\.(png|jpg|jpeg|gif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 5 * 1024
          }
        }
      },

      {
        test: /\.(woff2?|ttf|eot)$/i,
        type: 'asset/resource'
      }
    ]
  }
};

Почему шрифты редко inline

Шрифты:

  • обычно крупные;
  • используются на многих страницах;
  • хорошо кешируются браузером.

Поэтому чаще применяется:

type: 'asset/resource'

а не inline.


Использование функции вместо объекта

dataUrlCondition может быть функцией.

Пример:

{
  test: /\.png$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: (source, { filename }) => {
      return source.length < 3 * 1024;
    }
  }
}

Практическое применение функции

Функция позволяет:

  • учитывать путь файла;
  • проверять имя;
  • использовать разные лимиты;
  • анализировать содержимое.

Пример разных лимитов

{
  test: /\.(png|svg)$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: (source, { filename }) => {

      if (filename.includes('icons')) {
        return source.length < 15 * 1024;
      }

      return source.length < 4 * 1024;
    }
  }
}

Inline только для иконок

{
  test: /\.svg$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: (source, { filename }) => {
      return filename.includes('.icon.');
    }
  }
}

Полное отключение inline

Если необходимо всегда создавать отдельные файлы:

{
  test: /\.(png|jpg)$/i,
  type: 'asset/resource'
}

Либо:

{
  test: /\.(png|jpg)$/i,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 0
    }
  }
}

Принудительный inline

{
  test: /\.svg$/i,
  type: 'asset/inline'
}

В этом случае dataUrlCondition уже не используется.


Отличие asset от asset/inline

asset

Автоматический выбор:

inline или resource

asset/inline

Всегда inline:

data:image/png;base64,...

Отличие asset от asset/resource

asset

Автоматическое решение по размеру.


asset/resource

Всегда отдельный файл.


Влияние HTTP/2

Во времена HTTP/1.1 inline использовали значительно чаще, чтобы уменьшить количество запросов.

HTTP/2 изменил ситуацию:

  • multiplexing уменьшил стоимость запросов;
  • отдельные файлы стали менее проблемными;
  • крупный inline стал менее выгодным.

Поэтому современные конфигурации используют более консервативные лимиты.


Inline SVG без Base64

Webpack может использовать URI-кодирование вместо Base64 для SVG.

Это уменьшает размер строки.

Пример:

data:image/svg+xml,%3Csvg...

SVG особенно хорошо подходит для inline.


Производительность DevTools

При большом количестве inline-ресурсов:

  • sourcemaps становятся тяжелее;
  • DevTools медленнее открывают bundle;
  • сложнее анализировать network activity.

Анализ bundle

Для оценки влияния inline часто используют:

  • webpack-bundle-analyzer;
  • source-map-explorer;
  • Chrome Coverage;
  • Lighthouse.

Особенно важно отслеживать:

  • рост main bundle;
  • duplicated assets;
  • Base64 overhead.

Частые ошибки

Слишком высокий maxSize

Ошибка:

maxSize: 200 * 1024

Результат:

  • гигантские bundles;
  • плохая производительность.

Inline фотографий

JPEG и PNG-фотографии редко подходят для inline.

Плохая практика:

test: /\.(jpg|jpeg)$/i,
type: 'asset/inline'

Один лимит для всех форматов

SVG и PNG имеют разные характеристики.

Лучше разделять правила.


Оптимальная стратегия

На практике обычно используют:

Тип файла Стратегия
SVG-иконки inline
Маленькие PNG inline
Большие изображения resource
Шрифты resource
Видео resource

Пример сбалансированной конфигурации

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024
          }
        }
      },

      {
        test: /\.(png|jpg|jpeg|gif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024
          }
        }
      },

      {
        test: /\.(woff2?|ttf|eot)$/i,
        type: 'asset/resource'
      }
    ]
  }
};