写这篇文章的起因:团队里越来越多人往页面里塞 <img>、emoji、字体图标来占位,导致样式五花八门、加载慢、颜色对不齐。于是我开发了一个极简的 SVG 图标封装库,一个脚本文件 + 一行 data- 属性就能用,分享给各位作者。
svg-icons.js 是一个基于 jQuery 的轻量级 SVG 图标渲染器,核心特性:
整个库就 200 来行,把 comment、like、save、share、more、search、message、heat、view、time 等常用图标都内置好了。
<!-- 先 jQuery,再图标库 -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="script/svg-icons.js"></script><!-- 空元素:只放图标 -->
<i data-svg-icon="comment"></i>
<!-- 带文字:图标插在文字前面,文字保留 -->
<a data-svg-icon="featured">精选</a>
<!-- 带数字:常见于点赞 / 评论 -->
<li data-svg-icon="like">128</li>脚本在 DOMContentLoaded 时自动扫描 [data-svg-icon] 并渲染,无需任何 JS 调用。
效果等价于:
<i data-svg-icon="comment">
<svg class="icon" viewBox="0 0 1024 1024" ...>...</svg>
</i>图标会自动继承父元素的字体大小(1.1em)和颜色(currentColor)。
| 图标名 | 用途 | 尺寸 |
|---|---|---|
comment | 评论气泡 | 1.1em |
like | 点赞拇指 | 1.1em |
save | 收藏 / 星标 | 1.1em |
share | 分享 | 1.1em |
more | 更多(三个点) | 1.1em |
featured | 精选徽章(自带配色) | 1.8em |
menu | 汉堡菜单 | 24px |
search | 搜索放大镜 | 1.2rem |
message | 消息铃铛 | 1.5em |
heat | 热度火焰 | 1em |
view | 阅读量眼睛 | 1.1em |
time | 时钟 | 1.1em |
close / prev / next | 弹窗关闭 / 翻页 | 自适应 |
注意:收藏图标注册名是 save,不是 favorite。如果项目里习惯写 favorite,可以在脚本里加一行别名(见第七节)。
<div class="action-bar">
<span data-svg-icon="comment">12</span>
<span data-svg-icon="like">88</span>
<span data-svg-icon="save">6</span>
</div>打开就有图标,DOMContentLoaded 后自动渲染。
render这是最容易踩坑的地方。 AJAX 拉数据后用 html() / append() 插入的 [data-svg-icon] 不会自动渲染,因为自动渲染只跑一次。
正确姿势:
$.ajax({
url: '/api/posts',
success: function (res) {
var html = '';
res.data.forEach(function (item) {
html += '<li>' +
'<span data-svg-icon="like">' + item.like_count + '</span>' +
'<span data-svg-icon="comment">' + item.comment_count + '</span>' +
'</li>';
});
var $container = $('#list');
$container.html(html);
// ★ 关键一行:手动渲染动态插入的图标
window.svgIcon.render($container);
}
});render(container) 支持传:
// 只渲染某个卡片
svgIcon.render('.card');
// 只渲染某个 DOM
svgIcon.render(document.getElementById('content'));svgIcon 暴露两个方法:
svgIcon.render(container?)在指定容器内扫描 [data-svg-icon],把对应 SVG 前置插入到占位元素内。
svgIcon.addIcon(name, svgString)运行时注册新图标,适用于业务自定义图标或临时补齐:
svgIcon.addIcon('star', '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor"><path d="..."/></svg>');
// 之后即可使用
svgIcon.render();<i data-svg-icon="star"></i>currentColor图标统一使用 fill="currentColor",所以只要设置父元素的 color,图标自动变色:
css
.tab.active { color: #4510E6; } /* 图标同步变色 */
.btn-danger { color: #e33; }不需要给 SVG 单独写 fill。
font-size默认尺寸是 1.1em,同样跟随父元素字体大小:
.comment-item .icon { font-size: 14px; }或者用 em:
span[data-svg-icon] { font-size: 1.2em; }图标默认和文字在基线附近,建议给占位元素加 display:inline-flex; align-items:center;:
[data-svg-icon] {
display: inline-flex;
align-items: center;
gap: 4px;
vertical-align: middle;
}favorite vs save)如果你在业务里习惯写 data-svg-icon="favorite",但库里只有 save,就会报 SVG icon not found: favorite。
解决:在 svg-icons.js 的 icons 对象后加一行别名:
var icons = { /* ... */ };
// 别名:favorite → save
icons.favorite = icons.save;addIcon 也行:
svgIcon.addIcon('favorite', window.svgIcon.icons.save); // 不推荐,icons 未暴露所以推荐直接改源码加别名。
就是第四节说的:AJAX 插入后必须调用一次 svgIcon.render($container)。
建议封装一个小助手:
function renderIcons(container) {
if (window.svgIcon) window.svgIcon.render(container);
}不需要防抖。render 内部有幂等判断:
if ($el.children('svg').length > 0) return;滚动加载、重复调用都安全。
featured 图标里有写死的 fill="#50E3C2" / fill="#4510E6",不跟随 currentColor,这是有意设计的——精选徽章要固定配色。
如果需要它跟着文字变色,自己替换 SVG 里的 fill 即可。
打开 svg-icons.js,在 icons 对象里加一条:
var icons = {
// ... 现有图标
bell: '<svg viewBox="0 0 24 24" width="1.1em" height="1.1em" fill="currentColor"><path d="..."/></svg>'
};// 从后端或图标 API 拿到 SVG
fetch('/icons/star.svg')
.then(r => r.text())
.then(svg => {
svgIcon.addIcon('star', svg);
svgIcon.render();
});var customIcons = {
star: '<svg ...>',
heart: '<svg ...>',
bell: '<svg ...>'
};
Object.keys(customIcons).forEach(function (name) {
svgIcon.addIcon(name, customIcons[name]);
});
svgIcon.render();当前是 IIFE + 全局变量写法,适合传统多页应用(<script> 直接引入)。
如果未来要接 Vue / React / 原生 ESM,可以按这个思路改造:
// ESM 版本伪代码
export function renderIcons(container = document) { /* ... */ }
export function addIcon(name, svg) { /* ... */ }但在迁移前,现有全局 API 已足够覆盖绝大多数场景。
一个属性 data-svg-icon="xxx" 就能把任意元素变成 SVG 图标容器,动态内容插入后补一次 svgIcon.render(el) 就完事。
如果你也在维护一个多作者协作的前端项目,把这篇甩到群里,可以省掉 80% 关于"图标怎么用"的沟通成本。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
[data-svg-icon] {
display: inline-flex;
align-items: center;
gap: 4px;
color: #666;
font-size: 14px;
}
[data-svg-icon]:hover { color: #4510E6; }
</style>
</head>
<body>
<ul id="post-list"></ul>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="script/svg-icons.js"></script>
<script>
// 模拟数据
var posts = [
{ id: 1, likes: 128, comments: 32 },
{ id: 2, likes: 456, comments: 88 }
];
var html = posts.map(function (p) {
return '<li>' +
'<span data-svg-icon="like">' + p.likes + '</span>' +
'<span data-svg-icon="comment">' + p.comments + '</span>' +
'<span data-svg-icon="save"></span>' +
'<span data-svg-icon="share"></span>' +
'</li>';
}).join('');
$('#post-list').html(html);
svgIcon.render('#post-list'); // ← 动态内容关键一行
</script>
</body>
</html>希望这篇文章能让你在团队协作里少踩坑。有问题或想贡献新图标,欢迎在脚本里提 PR 👋
附:本文基于 svg-icons.js v1.0 编写,后续新增图标或 API 变更请同步更新本文档。
/**
* SVG 图标封装库
* 依赖: jQuery
* 用法:
* 1. 在页面中放置占位元素:<span data-svg-icon="comment"></span>
* 或带文字:<a data-svg-icon="featured">精选</a>
* 2. 引入 jQuery 和本文件,图标会自动渲染(不会清空元素内的文字或HTML)
* 3. 也可手动调用:window.svgIcon.render(selector)
* 4. 已渲染过的元素不会重复插入图标
*/
; (function ($, window, document, undefined) {
'use strict';
// ==================== 图标表 ====================
// 自行添加:key 是图标名,value 是完整 <svg> 字符串
// 建议:viewBox="0 0 1024 1024"、width/height="1.1em"、fill="currentColor"
var icons = {
// 示例:评论图标
comment: '<svg class="icon" viewBox="0 0 1024 1024" width="1.1em" height="1.1em" fill="currentColor"><path d="...在这里粘贴你的 path 数据..."/></svg>',
// 示例:更多(三个点)
more: '<svg class="icon" viewBox="0 0 1024 1024" width="1.1em" height="1.1em" fill="currentColor"><path d="..."/></svg>'
// 继续按上面的格式添加 like / save / share / search ...
};
// ==================== 渲染函数 ====================
// 将指定容器内所有 [data-svg-icon] 占位元素前置插入对应 SVG
function render(container) {
container = container || document;
$(container).find('[data-svg-icon]').each(function () {
var $el = $(this);
var iconName = $el.data('svg-icon');
// 幂等:元素内已插入过 svg 则跳过,重复调用不会叠图
if ($el.children('svg').length > 0) {
return;
}
if (icons[iconName]) {
// prepend:图标在前,原有文字/子元素全部保留
$el.prepend($(icons[iconName]));
} else {
console.warn('SVG icon not found: ' + iconName);
}
});
}
// ==================== 对外 API ====================
window.svgIcon = {
render: render,
// 运行时注册新图标
addIcon: function (name, svgString) {
icons[name] = svgString;
}
};
// ==================== 自动渲染 ====================
// DOM 就绪后扫描全文档,渲染初始占位元素
$(function () {
render();
});
})(jQuery, window, document);