npm package: @mureka-fe/icon-sdk
Description: Vue 3 图标 SDK，按图标名称在运行时拉取资源，不要求业务应用把全部图标打进构建产物。
Latest version: 1.1.1

# @mureka-fe/icon-sdk

Vue 3 图标 SDK，按图标名称在运行时拉取资源，不要求业务应用把全部图标打进构建产物。

它适合配合本仓库的图标管理后台使用，也可以单独接入任意提供兼容接口的服务端。

## 特性

- 运行时按名称加载图标
- CDN 优先，接口内联内容兜底
- 支持 SVG 和 WebP 两种最终资源类型
- 内置两级缓存，避免同名图标重复请求
- 识别软删除图标并渲染内置兜底图形

## 安装

```bash
pnpm add @mureka-fe/icon-sdk
```

## 快速开始

### 注册插件

```ts
import { createApp } from 'vue';
import { IconPlugin } from '@mureka-fe/icon-sdk';
import App from './App.vue';

const app = createApp(App);

app.use(IconPlugin, {
  apiBaseUrl: 'https://api.example.com',
  cdnBaseUrl: 'https://cdn.example.com',
});

app.mount('#app');
```

### 使用组件

```vue
<template>
  <Icon name="arrow-right" />
  <Icon name="loading" :size="18" :spin="true" />
</template>

<script setup lang="ts">
import { Icon } from '@mureka-fe/icon-sdk';
</script>
```

## 配置项

`IconPlugin` 接收一个可选配置对象：

| 字段         | 类型     | 说明                                           |
| ------------ | -------- | ---------------------------------------------- |
| `apiBaseUrl` | `string` | 图标接口基础地址，可为空；为空时走同源 `/api`  |
| `cdnBaseUrl` | `string` | CDN 基础地址，可为空；为空时只使用接口返回内容 |

类型定义见 [`src/plugins/icon-plugin.ts`](/Users/kl/Documents/work-space/project/project-friday/fe-infra-monorepo/packages/icon-sdk/src/plugins/icon-plugin.ts)。

## `Icon` 组件 Props

| 属性    | 类型      | 默认值         | 说明                 |
| ------- | --------- | -------------- | -------------------- |
| `name`  | `string`  | 必填           | 图标名称             |
| `size`  | `number`  | `16`           | 宽高，单位 px        |
| `color` | `string`  | 不传时保留原色 | 显式覆盖 SVG 颜色    |
| `spin`  | `boolean` | `false`        | 是否启用默认匀速旋转 |

## 运行时加载流程

SDK 的核心逻辑在 [`src/utils/icon-cache.ts`](/Users/kl/Documents/work-space/project/project-friday/fe-infra-monorepo/packages/icon-sdk/src/utils/icon-cache.ts)。

实际流程如下：

1. 检查内存缓存，命中则直接返回
2. 检查并发锁，命中则复用正在进行中的请求
3. 请求 `/api/icons/name/:name` 获取图标元数据
4. 如果接口返回了 `primaryResourceUrl` 或 `resourceUrls`，优先直接拉取真实资源地址
5. 如果仍未命中，且配置了 `cdnBaseUrl` 且接口返回了 `cdnPath`：
   - `assetType=svg` 时，从 CDN 拉取 SVG 文本
   - `assetType=webp` 时，从 CDN 拉取二进制并转成 data URL
6. 如果 CDN 失败，回退到接口返回的 `svgContent`
7. 写入内存缓存

## 服务端响应要求

SDK 期望接口至少返回以下字段：

```ts
interface IconAssetResponse {
  assetType: 'svg' | 'webp';
  primaryResourceUrl?: string | null;
  resourceUrls?: Partial<Record<'domestic' | 'overseas', string>>;
  cdnPath?: string | null;
  svgContent: string;
  isDeleted: boolean;
}
```

接口路径约定：

```text
GET {apiBaseUrl}/api/icons/name/{name}
```

如果未配置 `apiBaseUrl`，SDK 会请求当前域名下的 `/api/icons/name/{name}`。

## 资源渲染行为

### SVG

当最终内容是 SVG 时，组件内部使用 `v-html` 注入，并在运行时对 SVG 做一次轻量归一化。

默认样式：

- 外层容器宽高来自 `size`
- 内层 `svg` 默认会被重写为 `width="16" height="16"`，传入 `size` 时会使用对应值
- 不传 `color` 时，默认保留资源原始颜色
- 显式传入 `color` 时，非 `none` / 非渐变引用的 `fill`、`stroke` 会归一成 `currentColor`

### WebP

当最终资源类型是 `webp` 且内容不是 SVG 字符串时，组件会改用 `<img>` 渲染。

这意味着：

- `size` 同样有效
- `spin` 同样有效
- `color` 不会像 SVG 一样重写图像像素颜色

## 旋转控制

### 默认旋转

直接传 `spin` 即可：

```vue
<Icon name="loading" :size="18" spin />
```

SDK 会在运行时自动注入默认旋转动画，不需要额外引入 CSS 文件。

### 业务自定义旋转

如果业务要自定义动画，优先通过 `style` 或 `class` 覆盖：

```vue
<Icon
  name="loading"
  :size="18"
  spin
  style="--fe-icon-animation: business-spin 1.8s ease-in-out infinite alternate"
/>
```

```css
@keyframes business-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(180deg);
  }
}
```

如果业务直接传 `style="animation: ..."`，也可以完全覆盖默认旋转。

## 软删除兜底

当接口返回 `isDeleted=true` 时，SDK 不会继续尝试加载 CDN，而是直接返回一个内置删除态 SVG。

这样做的目的：

- 业务页面不会因为图标被后台删除而直接报错
- 删除态在视觉上可识别，便于发现问题

## 缓存机制

### 并发锁

`pendingRequests` 用来缓存同名图标的进行中请求，避免并发风暴。

例如一个列表页中同时渲染 50 个相同图标，也只会发起一次网络请求。

### 内存缓存

`memoryCache` 用来缓存已经加载完成的图标内容。

单页应用内切页后再次渲染同名图标，不会重复请求。

## 使用建议

### 建议 1：统一图标命名规范

推荐使用 kebab-case，例如：

- `arrow-right`
- `logo-fe-infra`
- `status-success`

### 建议 2：优先提供 CDN

虽然 SDK 支持内联兜底，但生产环境更推荐让服务端直接返回 `primaryResourceUrl/resourceUrls`。`cdnBaseUrl` 仅作为兼容 fallback 保留。

### 建议 3：把 API 和 CDN 版本策略交给服务端

SDK 不负责资源版本管理。是否通过 hash、CDN 路径、缓存头实现热更新，应该由服务端和存储层决定。

## 本地开发

在本仓库中调试 SDK：

```bash
pnpm --filter @mureka-fe/icon-sdk typecheck
pnpm --filter @mureka-fe/icon-sdk build
```

## 发布

当前包在 monorepo 中维护，但作为独立 npm 包发布。

推荐流程：

```bash
pnpm changeset
pnpm version-packages
pnpm --filter @mureka-fe/icon-sdk typecheck
pnpm --filter @mureka-fe/icon-sdk build
pnpm release
```

如果只想验证打包内容：

```bash
cd packages/icon-sdk
pnpm pack
```

## 导出内容

```ts
export { Icon } from '@mureka-fe/icon-sdk';
export { IconPlugin, ICON_CONFIG_KEY } from '@mureka-fe/icon-sdk';
export type { IconConfig } from '@mureka-fe/icon-sdk';
```