Введение
В современной веб-разработке 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 работает следующим образом:
- Она читает строку слева направо, пока встречаются цифры
- Как только встречается нецифровой символ, парсинг прекращается
- Возвращается целое число из распознанных цифр
Таким образом:
parseInt('500ms')
→ 500 (корректно)parseInt('0.5s')
→ 0 (так как точка не является цифрой)
В результате длительность становилась равной нулю, что и ломало всю анимацию.
Решения проблемы
1. Использование parseFloat вместо parseInt
this.#animationDuration = parseFloat(
getComputedStyle(document.body).getPropertyValue('--animation-duration')
);
Функция parseFloat
понимает десятичные точки и продолжает парсинг до первого нечислового символа:
parseFloat('500ms')
→ 500parseFloat('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, поможет избежать подобных проблем в будущем.
Выбирайте решение, которое лучше всего подходит для вашего проекта, учитывая его архитектуру и требования к производительности.
Вы можете оставить комментарий: