惰性模式
默认情况下,EffCSS 会立即生成 CSS:一旦调用实用工具,样式表规则就会立即写入 CSSOM。在惰性模式下,实际的 CSS 生成会被延迟——只有当生成的解析器被实际使用(调用或字符串强制转换)时,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' }
};
});每次调用都会立即生成一条规则,即使结果从未在页面上使用。
之后——受控的懒惰
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 不受到影响:它们返回的是纯值(字符串/对象),而不是解析器,因此它们仍然会立即生成。
之前 — 默认(立即生成)
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。