@qklinglingba/react-svg-theme
v1.0.0
Published
React SVG 图标一键换色。把设计师给的 SVG 文件通过 `fill="currentColor"` 机制换色,不依赖多套图。
Readme
@qklinglingba/react-svg-theme
React SVG 图标一键换色。把设计师给的 SVG 文件通过 fill="currentColor" 机制换色,不依赖多套图。
安装
npm install @qklinglingba/react-svg-theme使用
1. 准备 SVG 文件
让设计师在需要换色的元素上使用 fill="currentColor",不需要换色的正常写色值:
<!-- 要跟主题换色的 -->
<path fill="currentColor" d="..." />
<!-- 辅色(同色 + 透明度) -->
<circle fill="currentColor" fill-opacity="0.2" d="..." />
<!-- 不换色的(红色警告等) -->
<circle fill="#ff4d4f" d="..." />如果 SVG 是写死颜色的,用 CLI 批量转换:
npx @qklinglingba/react-svg-theme convert src/assets/image --colors "#151b26,#1369da"
2. 颜色管理(项目本地实现)
包不绑定任何颜色管理方案。推荐用 React Context:
// src/theme/index.ts
export const MY_COLORS = {
blue: '#1677ff',
green: '#52c41a',
orange: '#fa8c16',
} as const;
// src/theme/ThemeContext.tsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext({ colorHex: '#1677ff', setColor: (hex: string) => {} });
export const useTheme = () => useContext(ThemeContext);
export const ThemeProvider = ({ children, defaultColor = '#1677ff' }) => {
const [colorHex, setColor] = useState(defaultColor);
return <ThemeContext.Provider value={{ colorHex, setColor }}>{children}</ThemeContext.Provider>;
};// src/app.tsx
root.render(
<ThemeProvider defaultColor="#1677ff">
<App />
</ThemeProvider>
);3. 使用 SvgIcon
import { SvgIcon } from '@qklinglingba/react-svg-theme';
import { useTheme } from '@/theme/ThemeContext';
import iconSrc from '@/assets/image/icon.svg';
function Page() {
const { colorHex } = useTheme();
return (
<SvgIcon src={iconSrc} color={colorHex} size={48} />
);
}4. 切换颜色
function ColorPicker() {
const { colorHex, setColor } = useTheme();
return (
<div style={{ display: 'flex', gap: 8 }}>
{Object.entries(MY_COLORS).map(([name, hex]) => (
<div
key={name}
onClick={() => setColor(hex)}
style={{
width: 32, height: 32, borderRadius: '50%',
background: hex, cursor: 'pointer',
border: colorHex === hex ? '3px solid #000' : '3px solid transparent',
}}
/>
))}
</div>
);
}颜色存在 React Context 中,全局共享。A 页面换色,B 页面自动同步——无需额外逻辑。
API
<SvgIcon>
加载 SVG 文件并替换颜色。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| src | string | — | SVG 文件 URL(import 得到或直接传 URL) |
| color | string | — | 目标颜色 hex,如 #1677ff |
| size | number \| '100%' | — | 正方形简写;传 '100%' 表示填满父容器(父容器需有固定宽高) |
| width | number | — | 宽度(传 size 时忽略) |
| height | number | — | 高度(传 size 时忽略) |
| mode | 'img' \| 'inline' | 'img' | 渲染模式 |
| className | string | '' | 额外 class |
mode 详解:
| | img(默认) | inline |
|---|---|---|
| 渲染方式 | data URI → <img> | dangerouslySetInnerHTML 内联 SVG |
| DOM 节点 | 1 个 <img> | SVG 所有路径展开为真实 DOM |
| CSS 控制 | 只能控制容器尺寸 | 可控制每个路径样式 |
| 大量图标 | ✅ 推荐 | ⚠️ 50 个以内 |
| 浏览器兼容性 | 所有现代浏览器 | 所有现代浏览器 |
选择合适的模式:
- 页面上一次性展示 50 个以上图标 → img 模式
- 需要 CSS 动画/交互控制路径 → inline 模式
- 不确定 → 用 img 模式,不会错
size="100%" 示例:
<div style={{ width: 24, height: 24 }}>
<SvgIcon src={icon} color={colorHex} size="100%" mode="inline" />
</div>性能说明
渲染开销
每次颜色切换时,所有图标重新执行 fill/stroke 替换 + 渲染。50 个图标总计约 20-50ms(纯 CPU,无网络),用户无感知。
图像大小限制
| 文件大小 | 表现 | 建议 | |---|---|---| | ≤ 30KB | 瞬间完成 | 随便用 | | 30KB ~ 80KB | ~50-100ms | 正常使用 | | 80KB ~ 300KB | ~200-500ms | 少量可用,不推荐大量使用 | | > 300KB | 秒级阻塞 | 不要用,改用多套图方案 |
大图场景正确的做法:用 CLI 在构建时预处理,输出多份已换好色的 SVG 文件,运行时直接
<img src={...}>切换 URL,零 JS 开销。
缓存机制
fetch结果缓存在模块级Map中,同一图标首次加载后不再发网络请求- 但是刷新页面后缓存清空,重新进入页面需重新
fetch(命中浏览器 HTTP 缓存)
渲染模式对比
| 场景 | 推荐模式 |
|---|---|
| ≤ 20 个图标 | 两者都行 |
| 20 ~ 200 个图标 | img 模式 |
| > 200 个图标 | img 模式 + 分页 / 虚拟滚动 |
| 需要 CSS 动画/交互 | inline 模式 |
| SSR(Next.js App Router 等) | img 模式,组件需标记 'use client' |
兼容性
| 环境 | 支持情况 |
|---|---|
| 现代浏览器(Chrome/Firefox/Safari/Edge) | ✅ |
| React 18+ | ✅ |
| Next.js Pages Router | ✅(需 'use client') |
| Next.js App Router | ✅(需 'use client') |
| Remix | ✅(需 'use client') |
| React Native | ❌ 需要原生 DOM |
| IE 11 | ❌ |
运行时仅依赖 fetch、Map、btoa 等标准 Web API,无外部运行时依赖。
CLI
转换 SVG 文件中写死的颜色为 currentColor:
# 替换所有颜色
npx @qklinglingba/react-svg-theme convert src/assets/image
# 只替换指定色号
npx @qklinglingba/react-svg-theme convert src/assets/image --colors "#151b26,#1369da"原理
- 通过
import拿到 SVG 文件的 URL SvgIcon在浏览器端fetch文件内容- 将所有
fill="currentColor"/stroke="currentColor"替换为目标 hex 值 - 默认编码为 base64 data URI,通过
<img>渲染 mode="inline"时直接内联 SVG- 如果 SVG 缺少
viewBox,组件会自动从width/height推断补齐,确保跨构建工具兼容
