Ленивый режим
По умолчанию EffCSS генерирует CSS с опережением: правила таблицы стилей записываются в CSSOM сразу после вызова утилиты. В ленивом режиме фактическая генерация откладывается — CSS попадает в таблицу стилей только тогда, когда созданный резолвер фактически используется (вызывается или преобразуется в строку).
Ленивый режим доступен в двух вариантах, поэтому вы можете выбрать необходимый уровень контроля:
| Вариант | Триггер | Область действия |
|---|---|---|
| Управляемый | опционально подключаемые утилиты lazy* | для каждой утилиты, всегда ленивый |
| Уровень библиотеки | configure({ lazy: true }) | каждая генерирующая утилита, возвращающая резолвер |
Они работают независимо и могут быть объединены.
1. Управляемые — lazy* утилиты
Эти утилиты всегда ленивы, независимо от любой глобальной конфигурации. Каждый вызов возвращает решатель функций. Ничего не генерируется, пока вы:
- не вызовете резолвер —
fn()(илиfn(...args)), - не преобразуете его в строку —
`${fn}`илиString(fn).
Содержимое правила может быть передано либо в виде объекта, либо в виде функции, которая возвращает этот объект. Функция вычисляется лениво, при первом запуске.
| Утилита | Возвращает | Генерирует |
|---|---|---|
lazyClassName(rule) (или className.lazy(rule)) | () => string (имя класса) | одно анонимное правило класса |
lazyAttribute(rule) (или attribute.lazy(rule)) | () => object ({ 'data-…': '' }) | одно анонимное правило атрибута |
lazyClassNames(gen) (или classNames.lazy(gen)) | резолвер селекторов | таблица стилей селектора класса |
lazyAttributes(gen) (или attributes.lazy(gen)) | резолвер селекторов | таблица стилей селектора атрибута |
lazyCustomStyles(gen) (или customStyles.lazy(gen)) | резолвер селекторов | пользовательская таблица стилей |
До - незамедлительно
import { className, attribute, classNames } from 'effcss';
// CSS is written into the shared stylesheet right here
const centerCls = className({ margin: 'auto' });
const markerAttr = attribute({ fontWeight: 'bold' });
const utils = classNames<Utils>((selectors) => {
const { w } = selectors;
return {
[w.s]: { width: '12px' },
[w.m]: { width: '16px' }
};
});Every call produces a rule immediately, even if the result is never used on the page.
После - контролируемо
import { lazyClassName, lazyAttribute, lazyClassNames } from 'effcss';
// nothing is generated at creation time
const centerCls = className.lazy({ margin: 'auto' });
const markerAttr = attribute.lazy({ fontWeight: 'bold' });
const utils = classNames.lazy<Utils>((selectors) => {
const { w } = selectors;
return {
[w.s]: { width: '12px' },
[w.m]: { width: '16px' }
};
});
// ... later, where the value is actually consumed:
<div className={centerCls()} {...markerAttr()}> // generates the anonymous rules
<div className={utils({ w: 'm' })} /> // generates the stylesheetОбратите внимание, что все ленивые обработчики, даже lazyClassName (className.lazy) и lazyAttribute (attribute.lazy), возвращают функции. Для ленивых обработчиков отдельных правил это дает дополнительное преимущество — преобразование такого обработчика в строку возвращает правильный селектор, который можно использовать в других стилях:
const divider = className.lazy({
width: '100%',
height: '1px'
});
const layout = classNames.lazy<Layout>((selectors) => ({
[selectors.row]: {
display: 'flex'
},
// `divider` coerce to its real `.class` selector
// when `layout` will be used at the first time
[`& ${divider}`]: {
background: 'red'
}
}));Более того, эти функции могут принимать не только объект, но и генератор:
const divider = className.lazy(() => {
// you can create other global rules inside
// so that they are created the first time className is used
const width = variable('100%');
return {
width: width(),
height: '1px',
'&:hover': {
[width]: '50%'
}
};
});Важно:
className/attributeвозвращают значение, не являющееся функцией (string/object) — они не являются ленивыми и здесь не задействованы.
2. На уровне библиотеки — configure({ lazy: true })
configure({ lazy: true }) мгновенно переключает каждую вспомогательную функцию генерации, возвращающую обработчик функций на её ленивую версию:
variable, variables, animation, animations, layer, layers, font, fonts, classNames, attributes, customStyles.
Вы продолжаете писать тот же самый API — нет необходимости использовать «ленивые» имена. Каждая из этих утилит теперь откладывает генерацию до тех пор, пока не будет использован её результат.
className и attribute не затрагиваются: они возвращают обычные значения (string / object), а не резолверы, поэтому генерация продолжается немедленно.
До - незамедлительно (по умолчанию)
import { variable, animation, classNames } from 'effcss';
const shadowColor = variable('#58666d');
const spin = animation({ to: { transform: 'rotate(360deg)' } });
const utils = classNames<Utils>((selectors) => {
const { w } = selectors;
return {
[w.s]: { width: '12px' }
};
});После — configure({ lazy: true })
import { configure, variable, animation, classNames } from 'effcss';
configure({ lazy: true });
// same API, different timing
const shadowColor = variable('#58666d');
const spin = animation({ to: { transform: 'rotate(360deg)' } });
// nothing is written yet
const utils = classNames.lazy<Utils>((selectors) => {
const { w } = selectors;
return {
[w.s]: { width: '12px' }
};
});
// nothing is written yet
// CSS is produced only on first real use:
`${shadowColor()} or ${shadowColor}`
`${spin()} or ${spin}`
utils({ w: 's' })Для множественных утилит (variables, animations, layers, fonts, ...) весь набор прогревается одновременно: обращение к любому отдельному резолверу генерирует весь набор, сохраняя порядок детерминированным для гидратации SSR.
Примечания к поведению
- Триггеры прогрева — резолвер прогревается вызовом или преобразованием строки. Простое удержание (или вызов
.get()/.set()для переменных) не генерирует CSS. variable().get()/.set()— перед прогревомget()возвращает'', аset()ничего не делает; они становятся активными только после прогрева резолвера.update()— в ленивом режиме не изменяет переменные, которые еще не созданы.- Гидратация SSR — порядок генерации детерминирован (общие внутренние счетчики), поэтому сервер и клиент генерируют идентичные селекторы, пока они прогревают резолверы в одном и том же порядке.
- События
subscribe— событияEffCSSEventгенерируются в момент фактической генерации (прогрев резолвера), а не при первом вызове утилиты.
Выбор между двумя вариантами
- Используйте управляемые утилиты
lazy*, когда вам нужна ленивая генерация только для определенных правил, а остальные — для немедленной, или когда вы хотите гарантировать ленивую генерацию независимо от общеприложенийной настройки. - Используйте
configure({ lazy: true }), когда вам нужно широкое поведение только то, что используется во всей библиотеке без изменения кода, например, в сочетании сvite-plugin-effcssдля отправки только того CSS, который фактически отображается.