Проблема с CSS-переменными и минификацией: как 500ms превратилось в 0.5s и сломало анимацию

687306fbe75cbe0a2892034b3ceb05949ee8d229
Почему CSS-переменные ломаются после минификации в production? Разбираем проблему с преобразованием 500ms в 0.5s и предлагаем 4 рабочих решения для корректного парсинга значений в JavaScript

Введение

В современной веб-разработке CSS-переменные (кастомные свойства) стали неотъемлемой частью workflow. Они позволяют хранить значения в одном месте и повторно их использовать. Однако, когда эти переменные используются в JavaScript, могут возникнуть неожиданные проблемы, особенно при сборке проекта для production.

Исходная ситуация

В нашем проекте была определена CSS-переменная для длительности анимации:

:root {
    --animation-duration: 500ms;
}

В JavaScript мы получали это значение следующим образом:

this.#animationDuration = parseInt(
    getComputedStyle(document.body).getPropertyValue('--animation-duration')
);

В development-режиме всё работало идеально. Однако после сборки для production анимации перестали работать корректно.

Причина проблемы

Оказалось, что CSS-минификатор, используемый в production-сборке, оптимизировал единицы измерения, преобразовав 500ms в 0.5s. Хотя для CSS это эквивалентные значения, наш JavaScript-код обрабатывал их по-разному.

Функция parseInt в JavaScript работает следующим образом:

  1. Она читает строку слева направо, пока встречаются цифры
  2. Как только встречается нецифровой символ, парсинг прекращается
  3. Возвращается целое число из распознанных цифр

Таким образом:

  • parseInt('500ms') → 500 (корректно)
  • parseInt('0.5s') → 0 (так как точка не является цифрой)

В результате длительность становилась равной нулю, что и ломало всю анимацию.

Решения проблемы

1. Использование parseFloat вместо parseInt

this.#animationDuration = parseFloat(
    getComputedStyle(document.body).getPropertyValue('--animation-duration')
);

Функция parseFloat понимает десятичные точки и продолжает парсинг до первого нечислового символа:

  • parseFloat('500ms') → 500
  • parseFloat('0.5s') → 0.5

Однако это решение требует дополнительной обработки для приведения к миллисекундам, если значение в секундах.

2. Нормализация значения перед преобразованием

const val = getComputedStyle(document.body).getPropertyValue('--animation-duration');
this.#animationDuration = !val.includes('ms')
    ? parseFloat(durationValue) * 1000
    : parseFloat(durationValue);

Это решение обрабатывает как миллисекунды, так и секунды.

3. Запрет минификации CSS-переменных

В некоторых сборщиках можно настроить минификатор так, чтобы он не изменял значения определённых CSS-переменных. Например, в PostCSS можно использовать опции типа:

cssnano({
    preset: [
        'default',
        {
            cssDeclarationSorter: false,
            reduceTransforms: false
        }
    ]
})

4. Использование data-атрибутов вместо CSS-переменных

Альтернативный подход - хранить такие значения в data-атрибутах с последующей записью свойства для использования в CSS:

<body data-animation-duration="500">
this.#animationDuration = parseInt(document.body.dataset.animationDuration);
document.body.style.setProperty('--animation-duration', `${this.#animationDuration}ms`);

Рекомендации

  • Всегда тестируйте production-сборку - многие проблемы проявляются только после минификации и оптимизации кода.
  • Будьте осторожны с парсингом строк - учитывайте возможные форматы входных данных.
  • Документируйте ожидаемые форматы - если ваша функция ожидает только миллисекунды, укажите это в документации.
  • Рассмотрите альтернативные подходы - возможно, CSS-переменные не всегда лучший способ передачи данных в JavaScript.

Заключение

Проблема с преобразованием 500ms в 0.5s - отличный пример того, как оптимизации для production могут влиять на функциональность приложения. Понимание того, как работают парсеры в JavaScript и как минификаторы обрабатывают CSS, поможет избежать подобных проблем в будущем.

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

Вы можете оставить комментарий:

Поддерживается разметка markdown

Комментарий будет опубликован после модерации