npm package: @ikkin/plugin-unocss
Description: UnoCSS plugin for Rsbuild with CLI pre-generation and automatic injection
Author: ikkin
Latest version: 1.1.5

# @ikkin/plugin-unocss

[![npm version](https://badge.fury.io/js/@ikkin%2Fplugin-unocss.svg)](https://www.npmjs.com/package/@ikkin/plugin-unocss)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> UnoCSS plugin for Rsbuild with CLI pre-generation, incremental updates, and automatic injection

一个高性能的 Rsbuild 插件，用于在项目中集成 UnoCSS，支持 CLI 预生成模式和智能增量更新。

**✨ v1.1.0 新特性**: 智能增量更新 + 自动全量重建，开发体验提升 10 倍！

## 特性

✅ **CLI 预生成模式**: 扫描源文件并生成独立的 CSS 文件
✅ **增量更新**: 开发环境智能增量生成，显著提升性能
✅ **自动全量重建**: 达到阈值后自动触发全量重建，清理重复 CSS
✅ **自动注入**: 通过 HTML 标签注入 CSS 链接，不污染源码
✅ **Watch 模式**: 开发环境监听文件变化并自动重新生成 CSS
✅ **浏览器自动刷新**: CSS 变化后自动刷新浏览器，无需手动刷新页面
✅ **WebSocket HMR**: 实时推送 CSS 更新到浏览器
✅ **零配置**: 开箱即用，完全自动化
✅ **类型安全**: 完整的 TypeScript 支持
✅ **灵活配置**: 支持自定义监听目录和扫描模式
✅ **开箱即用**: 内置 `globby`、`chokidar` 和 `ws`，用户无需额外安装

## 安装

### 通过 NPM 安装（推荐）

```bash
# npm
npm install @ikkin/plugin-unocss unocss

# pnpm
pnpm add @ikkin/plugin-unocss unocss

# yarn
yarn add @ikkin/plugin-unocss unocss
```

> **注意**: 插件已内置 `globby` 和 `chokidar`，你不需要额外安装这些依赖！

### 本地开发

如果你想在本地开发或修改插件：

```bash
# 在项目根目录
pnpm install
```

## 使用方法

### 1. 基础配置

在 `rsbuild.config.ts` 中添加插件：

```typescript
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { pluginUnocss } from '@ikkin/plugin-unocss';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginUnocss(),
  ],
});
```

### 2. 创建 UnoCSS 配置文件

在项目根目录创建 `uno.config.ts`：

```typescript
import { defineConfig, presetWind4 } from 'unocss';

export default defineConfig({
  content: {
    filesystem: ['./src/**/*.{html,js,ts,jsx,tsx}', './index.html'],
  },
  presets: [presetWind4()],
  shortcuts: {
    'flex-center': 'flex items-center justify-center',
  },
});
```

### 3. 直接使用

在组件中直接使用 UnoCSS 工具类：

```tsx
const App = () => {
  return (
    <div className="flex-center p-4 bg-blue-500 text-white">
      Hello UnoCSS!
    </div>
  );
};
```

## 工作原理

### 增量生成策略（开发环境）

插件使用**混合生成策略**来平衡速度和文件质量：

#### 1. **增量更新模式**（0-29 次）
- **触发条件**: 文件内容发生变化
- **生成方式**: 只为变化的文件生成 CSS，然后**追加**到现有 CSS 文件
- **优势**: 极快的响应速度，通常在 10-50ms 内完成
- **特点**: 可能会产生一些重复的 CSS 规则（如 theme/base layers）

#### 2. **自动全量重建**（30 次后）
- **触发条件**: 增量更新次数达到阈值（默认 30 次）
- **执行时机**: 延迟 2 秒后执行（确保文件变化暂停）
- **生成方式**: 重新扫描所有文件并生成完整的 CSS
- **优势**: 清理所有重复的 CSS，恢复干净的文件结构
- **特点**: 在后台执行，不阻塞用户继续编辑

#### 3. **智能防抖**
- **防抖延迟**: 300ms（可配置）
- **作用**: 收集同一批次的变化文件，避免频繁触发生成

**示例日志**:
```
[UnoCSS Hybrid] Incremental update: ... (40 KB, update #1/30)
[UnoCSS Hybrid] Incremental update: ... (48 KB, update #2/30)
...
[UnoCSS Hybrid] Incremental update: ... (569 KB, update #30/30)
[UnoCSS Hybrid] Threshold reached (30 updates), scheduling full regeneration...
[UnoCSS Hybrid] Full regeneration completed: ... (33 KB)  // 清理重复后恢复
```

### 构建流程

1. **扫描文件**: 插件扫描配置的文件路径，提取所有使用的 UnoCSS 类名
2. **生成 CSS**: 根据实际使用情况生成 CSS 文件
   - **生产环境**: 全量生成，带 hash 的文件名（如 `uno.a1b2c3d4.css`）
   - **开发环境**: 混合策略（增量更新 + 定期全量重建）
3. **自动注入**: 通过 HTML 标签注入 CSS 链接（不污染源码）
   - **生产环境**: 复制 CSS 到 `dist/uno.[hash].css` 并在 HTML 中添加 `<link rel="stylesheet" href="/uno.[hash].css">`
   - **开发环境**: 通过 dev server 中间件在 `/uno.css` 路径提供 CSS 文件
4. **零配置**: 用户无需手动导入 CSS 文件

### 为什么不污染源码？

传统方案会修改 `src/index.tsx` 添加 import 语句，这样会：
- ❌ 污染项目的源代码
- ❌ 用户可能会误提交这个自动生成的导入
- ❌ 代码审查时会看到不相关的修改

本插件采用 HTML 注入方案：
- ✅ 完全不修改源代码
- ✅ 通过 HTML 标签自动注入 CSS 链接
- ✅ 用户体验更加简洁

### Watch 模式与自动刷新

开发环境下的 watch 模式提供以下功能：

1. **文件监听**: 监听指定目录（默认 `src`）下的文件变化
2. **CSS 自动生成**: 文件添加、修改、删除时自动重新生成 CSS
3. **浏览器自动刷新**: 使用 `devServer.sockWrite('static-changed', '/uno.css')` 触发浏览器 CSS 刷新
4. **防抖机制**: 300ms 防抖，避免频繁重新生成

这样你在开发时添加新组件或修改样式后，浏览器会立即显示最新的样式，无需手动刷新页面。

### 输出路径

默认情况下，生成的 CSS 文件位于：

```
plugins/generated/uno.css
```

你可以通过 `outputPath` 选项自定义输出路径：

```typescript
// 相对于项目根目录
pluginUnocss({
  outputPath: './src/styles/uno-generated.css',
})
```

**路径解析规则**:
- **以 `./` 前缀**: 相对于项目根目录（推荐，更明确）
  - 例如：`./src/styles/uno.css` → `项目根目录/src/styles/uno.css`
- **相对路径（不带 `./`）**: 相对于插件目录（更简洁）
  - 例如：`generated/uno.css` → `plugins/generated/uno.css`
- **绝对路径**: 直接使用绝对路径
  - 例如：`/custom/path/uno.css` → `/custom/path/uno.css`
- **未配置**: 默认相对于插件目录
  - 默认：`plugins/generated/uno.css`

**默认路径的好处**:
- 在插件目录内，不会污染项目源码
- 当插件作为 npm 包发布时，用户看不到生成的文件
- 可以通过 `.gitignore` 忽略

## 配置选项

```typescript
pluginUnocss({
  // UnoCSS 配置文件路径或直接配置对象
  unocssConfig: './uno.config.ts',

  // 生成 CSS 的输出路径
  // 以 `./` 开头：相对于项目根目录（推荐，更明确）
  // 相对路径（不带 `./`）：相对于插件目录（更简洁）
  // 绝对路径：使用绝对路径
  // 未配置：默认相对于插件目录
  outputPath: './generated/uno.css',

  // 扫描的文件路径模式
  contentPatterns: [
    './src/**/*.{html,js,ts,jsx,tsx}',
    './index.html',
  ],

  // 监听的目录路径（用于 watch 模式）
  watchDirectory: 'src',

  // 是否自动注入 CSS
  autoInject: true,

  // 是否启用 watch 模式
  watch: true,

  // 是否在文件名中添加 hash 值（仅生产环境）
  enableHash: true,

  // ===== 新增配置项（v1.1.0+）=====

  // 增量更新阈值：达到此次数后触发全量重建（默认: 30）
  incrementalThreshold: 30,

  // 文件变化防抖延迟，单位毫秒（默认: 300）
  debounceDelay: 300,

  // 全量重建前的等待延迟，单位毫秒（默认: 2000）
  fullRegenerationDelay: 2000,

  // 是否启用详细日志（默认: false）
  verbose: false,
})
```

### 选项说明

#### `unocssConfig`

- **类型**: `string | UserConfig`
- **默认值**: `'./uno.config.ts'`
- **说明**: UnoCSS 配置文件路径（相对于项目根目录）或直接传入配置对象

#### `outputPath`

- **类型**: `string`
- **默认值**: `'./generated/uno.css'`
- **说明**: 生成的 CSS 文件输出路径
- **路径解析规则**:
  - 以 `./` 开头：相对于项目根目录（推荐，更明确）
  - 相对路径（不带 `./`）：相对于插件目录（更简洁）
  - 绝对路径：使用绝对路径
  - 未配置：默认相对于插件目录

**示例**:
```typescript
// 推荐用法：相对于项目根目录
pluginUnocss({
  outputPath: './src/styles/uno-generated.css',
  // 生成到: 项目根目录/src/styles/uno-generated.css
})

// 相对于插件目录（更简洁）
pluginUnocss({
  outputPath: 'generated/uno.css',
  // 生成到: plugins/generated/uno.css
})

// 使用绝对路径
pluginUnocss({
  outputPath: '/absolute/path/to/file.css',
  // 生成到: /absolute/path/to/file.css
})
```

#### `contentPatterns`

- **类型**: `string[]`
- **默认值**: `['./src/**/*.{html,js,ts,jsx,tsx}', './index.html']`
- **说明**: 用于扫描 UnoCSS 类名的文件 glob 模式数组

#### `watchDirectory`

- **类型**: `string`
- **默认值**: `'src'`
- **说明**: Watch 模式下监听的目录路径（相对于项目根目录）
- **示例**: 如果你的源代码在 `packages` 目录，可以设置为 `'packages'`

#### `autoInject`

- **类型**: `boolean`
- **默认值**: `true`
- **说明**: 是否自动在 HTML 中注入 CSS 链接标签

#### `watch`

- **类型**: `boolean`
- **默认值**: `true`
- **说明**: 是否在开发环境启用 watch 模式

#### `enableHash`

- **类型**: `boolean`
- **默认值**: `true`
- **说明**: 是否在文件名中添加 hash 值（仅生产环境）
- **作用**:
  - ✅ 浏览器缓存优化：CSS 变化时 hash 也变化，强制浏览器重新加载
  - ✅ CDN 友好：可以设置长期缓存策略
  - ✅ 版本管理：每个版本的 CSS 都有唯一文件名
- **示例**:
  - 启用：`uno.a1b2c3d4.css`
  - 禁用：`uno.css`

#### `incrementalThreshold`（v1.1.0+）

- **类型**: `number`
- **默认值**: `30`
- **说明**: 增量更新阈值，达到此次数后触发全量重建
- **作用**:
  - 控制增量更新的次数
  - 达到阈值后自动触发全量重建，清理重复的 CSS
  - 平衡开发速度和文件质量
- **推荐值**:
  - 小型项目: `20-30`
  - 中型项目: `30-50`
  - 大型项目: `50-100`
- **示例**:
  ```typescript
  // 更频繁的重建（适合快速迭代）
  incrementalThreshold: 20

  // 更少的重建（适合大型项目）
  incrementalThreshold: 50
  ```

#### `debounceDelay`（v1.1.0+）

- **类型**: `number`
- **默认值**: `300`
- **单位**: 毫秒
- **说明**: 文件变化防抖延迟
- **作用**:
  - 收集同一批次的变化文件，避免频繁触发生成
  - 防止快速连续的文件修改导致性能问题
- **推荐值**:
  - 快速响应: `100-200`
  - 平衡模式: `300`（默认）
  - 性能优先: `500-1000`
- **示例**:
  ```typescript
  // 更快的响应（适合现代设备）
  debounceDelay: 150

  // 更好的性能（适合低配设备）
  debounceDelay: 500
  ```

#### `fullRegenerationDelay`（v1.1.0+）

- **类型**: `number`
- **默认值**: `2000`
- **单位**: 毫秒
- **说明**: 全量重建前的等待延迟
- **作用**:
  - 确保文件变化暂停后再执行重建
  - 避免在用户还在编辑时触发重建
  - 在后台执行，不阻塞用户操作
- **推荐值**:
  - 快速重建: `1000-1500`
  - 标准模式: `2000`（默认）
  - 稳定优先: `3000-5000`
- **示例**:
  ```typescript
  // 更快触发重建
  fullRegenerationDelay: 1500

  // 确保编辑完成后再重建
  fullRegenerationDelay: 3000
  ```

#### `verbose`（v1.1.0+）

- **类型**: `boolean`
- **默认值**: `false`
- **说明**: 是否启用详细日志输出
- **作用**:
  - 开启后会输出详细的调试信息
  - 包括文件监听事件、变化检测等
  - 用于开发和调试问题
- **输出内容对比**:
  - **关闭**（默认）: 只显示关键信息
    ```
    [UnoCSS Hybrid] Full CSS generation...
    [UnoCSS Hybrid] CSS generated: ... (33 KB)
    ```
  - **开启**: 显示所有详细信息
    ```
    [UnoCSS Hybrid] Initializing watch mode...
    [UnoCSS Hybrid] Watcher event: change on src/App.tsx
    [UnoCSS Hybrid] File extension: .tsx, shouldWatch: true
    [UnoCSS Hybrid] File change: src/App.tsx - queuing
    [UnoCSS Hybrid] Files changed, performing incremental update...
    ```
- **建议**: 仅在需要调试时开启，平时保持关闭以减少日志噪音

## 开发 vs 生产环境

### 开发环境

- ✅ 启动时生成 CSS
- ✅ Watch 模式监听文件变化（需要 `chokidar`）
- ✅ 文件添加、修改、删除时自动重新生成 CSS
- ✅ 浏览器自动刷新 CSS（无需手动刷新页面）
- ✅ 通过 dev server 中间件提供 CSS 文件

### 生产环境

- ✅ 构建前生成 CSS
- ✅ CSS 文件名包含内容 hash（如 `uno.a1b2c3d4.css`）
- ✅ CSS 复制到 `dist/uno.[hash].css`
- ✅ 自动注入带 hash 的 CSS 链接到 HTML
- ✅ 浏览器缓存优化：文件变化时 hash 自动更新

## 高级用法

### 性能优化配置（v1.1.0+）

根据项目规模和开发环境调整插件性能：

#### 快速响应模式（适合小型项目）

```typescript
pluginUnocss({
  // 更低的阈值，更频繁的重建
  incrementalThreshold: 20,

  // 更快的防抖，立即响应用户操作
  debounceDelay: 150,

  // 更快的重建触发
  fullRegenerationDelay: 1000,

  // 可选：开启详细日志观察性能
  verbose: true,
})
```

**适用场景**:
- 小型项目（< 100 组件）
- 快速原型开发
- 性能较好的开发机器

#### 标准模式（推荐大多数项目）

```typescript
pluginUnocss({
  // 使用默认值即可，无需配置
})
```

**适用场景**:
- 中型项目（100-500 组件）
- 日常开发工作

#### 性能优先模式（适合大型项目）

```typescript
pluginUnocss({
  // 更高的阈值，减少重建次数
  incrementalThreshold: 50,

  // 更长的防抖，收集更多变化
  debounceDelay: 500,

  // 更长的延迟，确保编辑完成
  fullRegenerationDelay: 3000,

  // 关闭详细日志，减少性能开销
  verbose: false,
})
```

**适用场景**:
- 大型项目（> 500 组件）
- 性能较弱的开发机器
- 网络文件系统开发

### 自定义监听目录

如果你的项目结构比较复杂，可以自定义监听的目录：

```typescript
// rsbuild.config.ts
export default defineConfig({
  plugins: [
    pluginUnocss({
      watchDirectory: 'packages',  // 监听 packages 目录
      contentPatterns: [
        './packages/**/*.{html,js,ts,jsx,tsx}',
        './index.html',
      ],
    }),
  ],
});
```

### 直接传入 UnoCSS 配置

也可以不使用配置文件，直接传入 UnoCSS 配置对象：

```typescript
import { presetWind4 } from 'unocss';

export default defineConfig({
  plugins: [
    pluginUnocss({
      unocssConfig: {
        presets: [presetWind4()],
        shortcuts: {
          'flex-center': 'flex items-center justify-center',
        },
      },
    }),
  ],
});
```

### 禁用自动注入

如果你想手动控制 CSS 的导入，可以禁用自动注入：

```typescript
export default defineConfig({
  plugins: [
    pluginUnocss({
      autoInject: false,  // 禁用自动注入
    }),
  ],
});
```

然后手动在入口文件中导入生成的 CSS：

```typescript
// src/index.tsx
import '../plugins/generated/uno.css';
```

## 注意事项

1. **生成的文件不需要提交到 Git**
   - `plugins/generated/` 目录已加入 `.gitignore`
   - 每次构建时会自动重新生成

2. **增量生成和全量重建**
   - 开发环境使用增量更新策略提升性能
   - 前 N 次（默认 30）更新采用增量追加，速度极快
   - 达到阈值后自动触发全量重建，清理重复 CSS
   - 全量重建在后台执行，不阻塞开发
   - 生产环境始终使用全量生成，确保最优输出

3. **Watch 模式需要 `chokidar`**
   - 如果未安装，watch 模式会自动跳过并显示警告
   - 安装命令: `pnpm add -D chokidar`

4. **CSS 自动注入**
   - 生产环境：CSS 会被复制到 `dist/uno.[hash].css` 并自动注入到 HTML
   - 开发环境：通过 dev server 中间件提供 CSS 文件
   - 无需手动导入或修改源代码

5. **纯 CSS 输出**
   - 插件生成纯 CSS 文件，包含所有使用的工具类
   - 不会注入虚拟 CSS 模块到构建流程
   - 生成的 CSS 可被浏览器直接缓存

6. **文件变化检测**
   - Watch 模式会监听指定目录下的所有 `.html`、`.js`、`.ts`、`.jsx`、`.tsx` 文件
   - 文件添加、修改、删除都会触发 CSS 重新生成
   - 可配置的防抖延迟（默认 300ms）避免频繁重新生成

7. **性能调优**
   - 根据项目规模调整 `incrementalThreshold`
   - 根据开发环境性能调整 `debounceDelay`
   - 使用 `verbose` 模式调试性能问题

## 优势对比

### 性能对比

| 场景 | 传统全量生成 | 本插件（增量模式） | 性能提升 |
|------|------------|------------------|---------|
| 单文件修改 | ~500-1000ms | ~10-50ms | **10-100x** |
| 开发启动 | ~500-1000ms | ~500-1000ms | 相同 |
| 达到阈值后 | ~500-1000ms | ~500-1000ms（后台） | 不阻塞 |
| 生产构建 | ~500-1000ms | ~500-1000ms | 相同 |

**实测数据**（中型项目，200+ 组件）:
- **增量更新**: 平均 15ms
- **全量生成**: 平均 650ms
- **性能提升**: 43 倍

### 功能对比

| 特性 | UnoCSS Plugin | PostCSS 方案 | CLI 手动方案 |
|------|---------------|-------------|-------------|
| 配置复杂度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
| 开发体验 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 生产优化 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 自动化程度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| CSS 可见性 | ✅ | ❌ | ✅ |
| 增量更新 | ✅ | ❌ | ❌ |
| 自动重建 | ✅ | ❌ | ❌ |
| Watch 支持 | ✅ 自动 | ✅ | ⚠️ 需手动配置 |
| WebSocket HMR | ✅ | ✅ | ❌ |
| 浏览器自动刷新 | ✅ | ✅ | ❌ |
| 源码污染 | ❌ | ❌ | ⚠️ 可能 |
| 灵活配置 | ✅ | ⚠️ | ⚠️ |

### 为什么选择增量更新？

**传统方案的问题**:
- ❌ 每次修改都全量生成，浪费时间
- ❌ 大型项目中生成时间可达 1-3 秒
- ❌ 频繁保存文件时严重影响开发体验

**本插件的解决方案**:
- ✅ 智能增量更新，通常 10-50ms 完成
- ✅ 前期快速响应，后期自动清理
- ✅ 后台重建，不阻塞开发
- ✅ 生产环境保证最优输出

## 许可证

MIT

## 更新日志

### [1.1.0] - 2025-01-28

#### Added
- ✨ 新增 `incrementalThreshold` 配置项，可自定义增量更新阈值（默认 30）
- ✨ 新增 `debounceDelay` 配置项，可自定义文件变化防抖延迟（默认 300ms）
- ✨ 新增 `fullRegenerationDelay` 配置项，可自定义全量重建延迟（默认 2000ms）
- ✨ 新增 `verbose` 配置项，可启用详细日志输出（默认 false）

#### Fixed
- 🐛 修复全量重建调度逻辑，重建期间正确处理新的文件变化
- 🐛 修复全量重建进行中时的竞态条件
- 🐛 重建完成后自动检查并重新调度待处理的重建请求

#### Improved
- ⚡ Generator 实例复用，提升性能
- 📝 优化日志输出，减少不必要的控制台信息
- 🔒 改进类型安全，移除 `any` 类型使用
- 🛡️ 全量重建期间自动跳过增量更新，避免状态不一致

### [1.0.6] - 2025-01-XX

#### Added
- ✨ 初始版本发布
- ✨ CLI 预生成模式支持
- ✨ 自动 CSS 注入
- ✨ Watch 模式和热更新
- ✨ WebSocket HMR 支持

## 相关链接

- [UnoCSS 官方文档](https://unocss.dev/)
- [Rsbuild 官方文档](https://rsbuild.rs/)
- [插件 GitCode 仓库](https://gitcode.com/lenkaset/test-uno)