npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eficy/plugin-unocss

v1.2.3

Published

UnoCSS plugin for @eficy/core-jsx

Readme

@eficy/plugin-unocss

UnoCSS plugin for @eficy/core-jsx - Automatically extracts and generates CSS styles from className attributes in Eficy components.

📖 概述

@eficy/plugin-unocss 是为 Eficy 框架 v3 设计的 UnoCSS 集成插件。它通过拦截组件渲染过程,自动提取 className 中的样式类,并使用 UnoCSS 生成对应的 CSS 样式,最终注入到页面中。该插件支持实时样式生成、缓存优化和自定义配置。

✨ 核心特性

🔍 自动样式提取

  • 智能识别: 自动从组件 props 中提取 className 属性
  • 深度扫描: 支持字符串、数组等多种 className 格式
  • 实时收集: 在组件渲染过程中实时收集样式类
  • 去重处理: 自动去除重复的样式类

⚡ 性能优化

  • 智能缓存: 基于样式类哈希的缓存机制,避免重复生成
  • 按需生成: 只生成实际使用的样式
  • 异步处理: 使用 @eficy/reactive-async 进行异步样式生成
  • 防抖优化: 1ms 防抖处理,避免频繁更新

🎨 UnoCSS 集成

  • 完整支持: 支持 UnoCSS 的所有特性和语法
  • 预设集成: 内置 Uno 和 Attributify 预设
  • 自定义配置: 支持完全自定义的 UnoCSS 配置
  • 错误处理: 优雅处理 CSS 生成失败的情况

🔌 插件化架构

  • 生命周期钩子: 集成到 Eficy 的插件生命周期系统
  • 优先级控制: 支持插件执行优先级设置
  • 依赖注入: 基于 tsyringe 的依赖注入支持
  • 热插拔: 支持插件的动态安装和卸载

📦 安装

npm install @eficy/plugin-unocss @unocss/core @unocss/preset-uno @unocss/preset-attributify
# 或
yarn add @eficy/plugin-unocss @unocss/core @unocss/preset-uno @unocss/preset-attributify
# 或
pnpm add @eficy/plugin-unocss @unocss/core @unocss/preset-uno @unocss/preset-attributify

🚀 快速开始

基础使用

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Eficy, EficyProvider } from '@eficy/core-jsx';
import { UnocssPlugin } from '@eficy/plugin-unocss';
import { signal } from '@eficy/reactive';

// 创建 Eficy 实例
const core = new Eficy();

// 安装 UnoCSS 插件
await core.install(UnocssPlugin);

// 应用组件
const App = () => {
  const count = signal(0);

  return (
    <div className="min-h-screen bg-gray-100 flex items-center justify-center">
      <div className="bg-white rounded-lg shadow-lg p-8">
        <h1 className="text-3xl font-bold text-gray-800 mb-4">Eficy + UnoCSS</h1>
        <p className="text-lg text-gray-600 mb-6">
          Count: <span className="font-semibold text-blue-600">{count}</span>
        </p>
        <button
          className="bg-blue-500 hover:bg-blue-600 text-white font-medium px-4 py-2 rounded transition-colors"
          onClick={() => (count.value += 1)}
        >
          Increment
        </button>
      </div>
    </div>
  );
};

// 渲染应用
const root = createRoot(document.getElementById('root'));
root.render(
  <EficyProvider core={core}>
    <App />
  </EficyProvider>,
);

自定义配置

import { UnocssPlugin } from '@eficy/plugin-unocss';
import { presetWind } from '@unocss/preset-wind';

// 使用自定义 UnoCSS 配置
await core.install(UnocssPlugin, {
  config: {
    presets: [
      presetWind(), // 使用 Tailwind CSS 兼容预设
    ],
    rules: [
      // 自定义规则
      [
        'btn-custom',
        {
          padding: '12px 24px',
          borderRadius: '8px',
          fontWeight: '500',
          transition: 'all 0.2s ease-in-out',
        },
      ],
    ],
    shortcuts: [
      // 自定义快捷方式
      ['btn-primary', 'btn-custom bg-blue-500 text-white hover:bg-blue-600'],
      ['btn-secondary', 'btn-custom bg-gray-200 text-gray-800 hover:bg-gray-300'],
    ],
    theme: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          900: '#1e3a8a',
        },
      },
    },
  },
});

🛠️ API 文档

UnocssPlugin 类

主要的插件类,实现了 ILifecyclePlugin 接口。

@injectable()
export class UnocssPlugin implements ILifecyclePlugin {
  public readonly name = 'unocss-plugin';
  public readonly version = '1.0.0';
  public readonly enforce = 'pre';

  // 插件配置
  async initialize(config?: UnocssPluginConfig): Promise<void>;

  // 渲染钩子
  onRender(context: IRenderContext, next: () => ComponentType<any>): ComponentType<any>;

  // 根组件挂载钩子
  onRootMount(context: IRenderContext, next: () => ComponentType<any>): ComponentType<any>;

  // 获取 UnoCSS 生成器实例
  getGenerator(): UnoGenerator | null;

  // 获取收集到的样式类
  getCollectedClasses(): Set<string>;

  // 清理资源
  destroy(): void;
}

UnocssPluginConfig 接口

interface UnocssPluginConfig {
  config?: UserConfig; // UnoCSS 自定义配置
}

配置选项

UnoCSS 配置支持所有官方配置选项:

{
  config: {
    // 预设
    presets: [
      presetUno(),
      presetAttributify(),
      presetWind(),
      // ... 其他预设
    ],

    // 自定义规则
    rules: [
      ['btn', { padding: '0.5rem 1rem' }],
      [/^m-(\d+)$/, ([, d]) => ({ margin: `${d}px` })],
    ],

    // 快捷方式
    shortcuts: [
      ['btn', 'px-4 py-2 rounded'],
      ['btn-primary', 'btn bg-blue-500 text-white'],
    ],

    // 主题
    theme: {
      colors: {
        primary: '#3b82f6',
        secondary: '#64748b',
      },
    },

    // 变体
    variants: [
      // 自定义变体
    ],

    // 预检样式
    preflights: [
      // 自定义预检样式
    ],
  }
}

🔧 工作原理

1. 初始化阶段

@Initialize()
async initialize(config: UnocssPluginConfig = {}) {
  // 创建 UnoCSS 生成器
  const generator = await createGenerator({
    presets: [
      presetUno({ preflight: false }),
      presetAttributify(),
    ],
    ...config.config,
  });

  // 初始化异步信号
  this.reactiveAsync = asyncSignal(
    () => this.generateCSS(),
    { manual: true, debounceWait: 1 }
  );
}

2. 样式收集阶段

@Render(5) // 优先级为 5,确保早期执行
onRender(context: IRenderContext, next: () => ComponentType<any>) {
  // 收集 className 中的样式类
  if (context.props.className) {
    this.collectClassNames(context.props.className);
  }

  return next();
}

3. CSS 生成阶段

private async generateCSS(): Promise<string | null> {
  const classArray = Array.from(this.collectedClasses).sort();
  const currentClassHash = classArray.join('|');

  // 检查缓存
  if (this.cssCache.has(currentClassHash)) {
    return this.cssCache.get(currentClassHash);
  }

  // 生成新的 CSS
  const result = await this.generator.generate(classArray.join(' '));
  const css = result.css;

  // 更新缓存
  if (css) {
    this.cssCache.set(currentClassHash, css);
  }

  return css;
}

4. 样式注入阶段

@RootMount()
onRootMount() {
  // 触发 CSS 生成
  this.reactiveAsync.run();
}

// Unocss 组件负责实际的样式注入
export const Unocss = ({ generateCSS }) => {
  const inlineStyle = useObserver(() => generateCSS.data);

  if (!inlineStyle) {
    return null;
  }

  return <style dangerouslySetInnerHTML={{ __html: inlineStyle }} id="unocss-styles" />;
};

🎯 高级特性

动态样式支持

const App = () => {
  const isDark = signal(false);
  const theme = computed(() => (isDark.value ? 'dark' : 'light'));

  return (
    <div className={`${theme.value}-theme min-h-screen transition-colors`}>
      <button
        className={`px-4 py-2 rounded ${isDark.value ? 'bg-white text-black' : 'bg-black text-white'}`}
        onClick={() => (isDark.value = !isDark.value)}
      >
        Toggle Theme
      </button>
    </div>
  );
};

条件样式类

const StatusBadge = ({ status }) => {
  const statusClasses = computed(() => {
    switch (status.value) {
      case 'success':
        return 'bg-green-500 text-white';
      case 'warning':
        return 'bg-yellow-500 text-black';
      case 'error':
        return 'bg-red-500 text-white';
      default:
        return 'bg-gray-500 text-white';
    }
  });

  return <span className={`px-2 py-1 rounded text-sm ${statusClasses.value}`}>{status}</span>;
};

响应式样式

const ResponsiveCard = () => (
  <div className="w-full sm:w-1/2 lg:w-1/3 xl:w-1/4 p-4">
    <div className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow p-6">
      <h3 className="text-lg sm:text-xl font-semibold mb-2">Card Title</h3>
      <p className="text-gray-600 text-sm sm:text-base">Card content...</p>
    </div>
  </div>
);

🔍 最佳实践

1. 性能优化

// ✅ 推荐:使用计算属性缓存复杂的样式计算
const complexClasses = computed(() => buildComplexClassString(props.value, state.value));

// ✅ 推荐:避免在渲染函数中创建新的样式字符串
const Component = () => {
  const baseClasses = 'px-4 py-2 rounded';
  const variantClasses = computed(() =>
    variant.value === 'primary' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800',
  );

  return <button className={`${baseClasses} ${variantClasses.value}`}>Button</button>;
};

2. 类型安全

// 定义样式类型
type ButtonVariant = 'primary' | 'secondary' | 'danger';
type ButtonSize = 'sm' | 'md' | 'lg';

interface ButtonProps {
  variant?: ButtonVariant;
  size?: ButtonSize;
  children: React.ReactNode;
}

const Button: React.FC<ButtonProps> = ({ variant = 'primary', size = 'md', children }) => {
  const variantClasses = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-500 text-white hover:bg-red-600',
  };

  const sizeClasses = {
    sm: 'px-2 py-1 text-sm',
    md: 'px-4 py-2',
    lg: 'px-6 py-3 text-lg',
  };

  return (
    <button className={`rounded transition-colors ${variantClasses[variant]} ${sizeClasses[size]}`}>{children}</button>
  );
};

3. 主题支持

// 定义主题配置
const lightTheme = {
  bg: 'bg-white',
  text: 'text-gray-900',
  border: 'border-gray-200',
};

const darkTheme = {
  bg: 'bg-gray-900',
  text: 'text-gray-100',
  border: 'border-gray-700',
};

const App = () => {
  const isDark = signal(false);
  const theme = computed(() => (isDark.value ? darkTheme : lightTheme));

  return (
    <div className={`min-h-screen transition-colors ${theme.value.bg} ${theme.value.text}`}>
      <div className={`border-b ${theme.value.border} p-4`}>
        <h1 className="text-2xl font-bold">Theme Example</h1>
      </div>
    </div>
  );
};

🧪 测试

单元测试示例

import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { Eficy, EficyProvider } from '@eficy/core-jsx';
import { UnocssPlugin } from '@eficy/plugin-unocss';

describe('UnocssPlugin', () => {
  it('should generate CSS for className attributes', async () => {
    const core = new Eficy();
    await core.install(UnocssPlugin);

    const TestComponent = () => <div className="text-red-500 p-4">Test</div>;

    render(
      <EficyProvider core={core}>
        <TestComponent />
      </EficyProvider>,
    );

    // 检查是否生成了对应的 CSS
    const styleElement = document.getElementById('unocss-styles');
    expect(styleElement).toBeTruthy();
    expect(styleElement?.innerHTML).toContain('.text-red-500');
    expect(styleElement?.innerHTML).toContain('.p-4');
  });
});

🔧 故障排除

常见问题

  1. 样式未生成

    • 确保插件已正确安装
    • 检查 className 是否正确传递
    • 验证 UnoCSS 配置是否正确
  2. 样式冲突

    • 检查 CSS 优先级
    • 确保插件执行顺序正确
    • 使用 !important 或更具体的选择器
  3. 性能问题

    • 检查是否有大量动态样式类
    • 优化样式类的使用方式
    • 考虑使用 CSS 变量代替动态类

📦 相关包

🤝 贡献

欢迎提交 Issue 和 Pull Request!

📄 许可证

MIT License