Skip to content

图标系统开发说明

图标系统的开发、维护和扩展指南。

系统架构

核心组件

  1. 构建脚本generate-icon-components.ts - SVG 转 Vue 组件
  2. SVGO 优化:自动优化 SVG,移除冗余代码
  3. 包装组件:生成带 props 的 Vue 组件
  4. 类型系统:自动生成 TypeScript 声明
  5. QxsIcon 组件@qxs-bns/components 中的智能图标组件

目录结构

packages/icons/
├── build/
│   └── generate-icon-components.ts  # 构建脚本
├── src/
│   ├── components/                  # 生成的组件
│   │   ├── Home.vue
│   │   └── ...
│   └── svg/                        # SVG 源文件
│       ├── home.svg
│       └── ...
├── typings/                        # 类型声明
├── index.ts                        # 导出文件
└── package.json

添加新图标

1. 准备 SVG 文件

将 SVG 文件放入 packages/icons/src/svg/ 目录:

bash
cp new-icon.svg packages/icons/src/svg/

2. 生成组件

bash
# 只生成组件(开发时)
pnpm -F @qxs-bns/icons generate-components

# 完整构建(生成组件 + 打包)
pnpm -F @qxs-bns/icons build

# 启用监听模式(开发时推荐)
pnpm -F @qxs-bns/icons dev

3. 验证结果

生成后的组件可以立即使用:

vue
<script setup lang="ts">
import { NewIcon } from '@qxs-bns/icons'
</script>

<template>
  <NewIcon :size="24" color="#409EFF" />
</template>

构建流程

组件生成(generate-components)

  1. 扫描 SVG:读取 src/svg/ 目录下所有 .svg 文件
  2. SVGO 优化:优化 SVG 内容,移除冗余属性
  3. 生成组件:为每个 SVG 创建 Vue 组件
  4. 类型声明:生成 TypeScript 类型文件
  5. 更新导出:更新 index.ts 导出列表

完整构建(build)

  1. 执行组件生成:运行 generate-components
  2. Rollup 打包:执行 rollup -c 生成分发文件
    • 生成 ES 模块(es/
    • 生成 CommonJS 模块(lib/
    • 生成类型声明文件(types/

开发命令

bash
# 完整构建(生成组件 + 打包)
pnpm -F @qxs-bns/icons build

# 只生成组件(不打包)
pnpm -F @qxs-bns/icons generate-components

# 监听模式(开发时推荐)
pnpm -F @qxs-bns/icons dev

# 清理生成的文件
pnpm -F @qxs-bns/icons clean

SVG 文件规范

基本要求

  1. 必须包含 viewBox

    xml
    <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  2. 避免固定尺寸

    xml
    <!-- ❌ 错误 -->
    <svg width="24" height="24" viewBox="0 0 24 24">
    
    <!-- ✅ 正确 -->
    <svg viewBox="0 0 24 24">
  3. 颜色继承

    xml
    <!-- ✅ 推荐 -->
    <path fill="currentColor" d="..."/>
    
    <!-- ✅ 可选 -->
    <path d="..."/>
    
    <!-- ❌ 避免 -->
    <path fill="#333333" d="..."/>
  4. 避免 ID 冲突

    xml
    <!-- ❌ 错误 -->
    <defs><linearGradient id="grad1">...</linearGradient></defs>
    
    <!-- ✅ 正确 -->
    <defs><linearGradient>...</linearGradient></defs>

命名规范

  • 文件名:kebab-case(如 arrow-down.svg
  • 组件名:PascalCase(如 ArrowDown
SVG 文件名组件名
home.svgHome
arrow-down.svgArrowDown
user-profile.svgUserProfile

SVGO 优化配置

构建脚本自动优化 SVG:

  • 移除 XML 声明和注释
  • 移除固定尺寸,添加响应式属性
  • 将颜色值替换为 currentColor
  • 压缩路径数据和空白

配置位于 build/generate-icon-components.ts

js
{
  plugins: [
    'preset-default',
    'removeDimensions',
    {
      name: 'addAttributesToSVGElement',
      params: {
        attributes: [
          { width: '1em' },
          { height: '1em' },
          { fill: 'currentColor' }
        ]
      }
    }
  ]
}

问题排查

图标不显示

bash
# 检查组件是否存在
ls packages/icons/src/components/

# 重新构建
pnpm -F @qxs-bns/icons build

# 检查导入语法
import { Home } from '@qxs-bns/icons'  # ✅ 正确
import Home from '@qxs-bns/icons/Home'  # ❌ 错误

样式问题

vue
<template>
  <!-- Props 控制(包装组件) -->
  <Home color="#409EFF" :size="24" />

  <!-- CSS 控制 -->
  <div style="color: #409EFF; font-size: 24px;">
    <HomeIcon />
  </div>

  <!-- 强制覆盖 -->
  <HomeIcon class="custom-icon" />
</template>

<style scoped>
.custom-icon {
  color: #409EFF;
  font-size: 24px;
}
.custom-icon :deep(svg *) {
  fill: currentColor !important;
}
</style>

构建问题

bash
# 检查依赖
pnpm list svgo lodash-es chokidar

# 重新安装
pnpm install

# 清理重建
rm -rf packages/icons/src/components
pnpm -F @qxs-bns/icons build

TypeScript 问题

bash
# 检查类型文件
ls packages/icons/typings/

# 重启 TS 服务
# VS Code: Ctrl+Shift+P -> "TypeScript: Restart TS Server"

QxsIcon 组件开发

组件架构

QxsIcon 组件位于 packages/components/src/icon/src/icon.vue,具有以下特性:

  1. 智能类型识别:自动识别图标类型(Vue组件、Iconify、UnoCSS、图片、CSS类、SVG Sprite)
  2. 统一样式系统:支持尺寸、颜色、旋转、翻转等样式定制
  3. 图片加载管理:提供加载状态和错误处理
  4. 类型安全:完整的 TypeScript 支持

添加新功能

  1. 新增图标类型支持

    typescript
    // 在 outputType computed 中添加新的识别逻辑
    const outputType = computed(() => {
      // 添加新的图标类型识别
      if (newIconTypePattern.test(iconStr)) {
        return 'new-type'
      }
      // ...
    })
  2. 扩展样式功能

    typescript
    // 在 style computed 中添加新的样式处理
    const style = computed(() => {
      // 添加新的样式计算逻辑
      const newStyleFeature = processNewStyle()
      return `${baseStyle}${newStyleFeature}`
    })
  3. 更新模板

    vue
    <template>
      <!-- 添加新图标类型的渲染逻辑 -->
      <new-icon-renderer
        v-else-if="outputType === 'new-type'"
        :icon="outputName"
        class="size-inherit shrink-0"
      />
    </template>

测试新功能

bash
# 运行组件测试
pnpm test:unit packages/components/src/icon

# 运行文档演示
pnpm docs:dev

# 类型检查
pnpm lint:tsc

维护指南

定期任务

  1. 更新依赖

    bash
    pnpm update svgo @types/lodash-es
  2. 优化检查

    bash
    # 检查生成的组件大小
    du -sh packages/icons/src/components/
    
    # 检查 SVG 优化效果
    ls -la packages/icons/src/svg/
  3. 性能监控

    bash
    # 构建时间
    time pnpm -F @qxs-bns/icons build

版本发布

bash
# 构建
pnpm -F @qxs-bns/icons build

# 测试
pnpm -F @qxs-bns/icons test

# 发布
pnpm -F @qxs-bns/icons publish

Released under the MIT License.