Skip to content

惰性模式

默认情况下,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)选择器解析器一个自定义样式表

之前 - 立即

ts
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' }
    };
});

每次调用都会立即生成一条规则,即使结果从未在页面上使用

之后——受控的懒惰

ts
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),都会返回函数。对于单个规则的惰性求值解析器,这带来了一个额外的好处——将此类解析器强制转换为字符串会返回正确的选择器,该选择器可用于其他样式:

ts
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'
    }
}));

此外,这些函数不仅可以接受对象,还可以接受生成器:

ts
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 *相同​​,无需使用“惰性求值”命名。现在,每个实用工具都会延迟生成,直到使用其结果为止。

classNameattribute 受到影响:它们返回的是纯值(字符串/对象),而不是解析器,因此它们仍然会立即生成。

之前 — 默认(立即生成)

ts
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 }) 之后

ts
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' })

对于多个实用工具(variablesanimationslayersfonts 等),整个批次会一起预热:操作任何一个解析器都会生成整个集合,从而保证 SSR 渲染的顺序确定性。

行为说明

  • 预热触发条件 — 解析器通过调用字符串强制转换进行预热。仅仅持有解析器(或对变量调用 .get() / .set()不会生成任何 CSS。
  • variable().get() / .set() — 在预热之前,get() 返回 ''set() 不执行任何操作;它们仅在解析器预热后才会生效。
  • update() — 在惰性模式下,不会更改尚未创建的变量。
  • SSR 水合 — 生成顺序是确定的(共享内部计数器),因此只要服务器和客户端以相同的顺序预热解析器,它们就会生成相同的选择器。
  • subscribe 事件EffCSSEvent 事件在实际生成(解析器预热)时触发,而不是在首次调用该工具时触发。

两者之间的选择

  • 如果您只想对特定规则使用延迟加载,而其余规则保持即时加载,或者您希望独立于应用程序全局设置来保证延迟加载,请使用受控的 lazy* 工具
  • 如果您希望在整个库中实现仅使用实际使用的行为,而无需更改代码,请使用**configure({ lazy: true })**,例如,结合 vite-plugin-effcss 仅发送实际渲染的 CSS。

根据 Apache-2.0 许可证发布。