符号复用与use
symbol 定义一份几何;use 实例化。生产默认是 同文档 inline sprite,不是每个图标一个 HTTP,也不是外部 use。
1. symbol + use
<svg>
<defs>
<symbol id="plus" viewBox="0 0 24 24">
<path d="M11 5h2v14h-2zM5 11h14v2H5z"/>
</symbol>
</defs>
<use href="#plus" x="8" y="8" width="24" height="24" fill="currentColor"/>
</svg>
新代码用 href。xlink:href 只在读入旧文件时识别,写出时改写掉。
export function instantiateSymbol(
parent: SVGElement,
symbolId: string,
box: { x: number; y: number; width: number; height: number },
): SVGUseElement {
const use = document.createElementNS("http://www.w3.org/2000/svg", "use");
use.setAttribute("href", `#${symbolId}`);
use.setAttribute("x", String(box.x));
use.setAttribute("y", String(box.y));
use.setAttribute("width", String(box.width));
use.setAttribute("height", String(box.height));
parent.append(use);
return use;
}
symbol 自己不画。缺 viewBox 时 use 的 width/height 对不齐,图标会被切片或挤扁。实例要缩放,靠 use 的宽高或外层 transform,不要去改 symbol 内部数字。
fill 写在 use 上能覆盖 内部未指定 fill 的图形(通过继承)。内部写死 #000 的 path 不会跟主题走——sprite 源文件用 currentColor 或留 fill="inherit"。
2. Shadow tree
SVG 2 把 use 的克隆做成 shadow tree(闭包)。后果:
use.querySelector("path")拿不到实例内部。不要靠穿透 DOM 去改「这一个」叶子。- 事件在宿主
use上重定向。监听绑在use上,不要绑 symbol 里的节点。 - 实例特定样式:改
use的 presentation / CSS 类。必须每实例不同几何时,那就不是 symbol 问题,应该复制成真正的 DOM。
Chrome / Firefox / Safari 对 shadow 封闭性、CSS ::part、getBBox 是否含 clone 仍有差。生产把 use 当 原子图元:命中、选中、变换都以 use 为对象。
3. 外部 use:不要当默认
href="https://cdn.example/icons.svg#plus" 或 href="/sprite.svg#plus":
- 跨域必须 CORS,失败时静默空白。
- 历史 WebKit / Safari 对外部
use支持差,同一文档引用才稳。 - sprite 文件里的脚本 / 样式与 inline 同一 XSS 面,CDN 不等于沙箱。
适用:同源、且你已为最差引擎做过功能检测的内部工具。
不适用:对外产品图标系统、邮件、要离线的编辑器文档。
4. Inline sprite 是生产默认
把所有 symbol 放进页面里一棵隐藏 <svg>(display: none 会让部分引擎 getBBox 异常,更稳是 width="0" height="0" aria-hidden="true" 且仍在文档里),或直接放在用到它的那棵图的 <defs>。
| 方案 | 适用 | 不适用 |
|---|---|---|
同文档 <symbol> + <use href="#id"> | 图标、可复用节点模板 | 每实例要改内部几何 |
| 复制节点 / 自建 path | 编辑器里的真正对象 | 工具栏 200 个静态 icon |
外部 use | 受控同源实验 | 默认交付路径 |
<img src="icon.svg"> | 静态、无需改色、无需命中内部 | 要 currentColor、要无障碍树 |
同一 symbol 实例化成千上万次仍是成千上万个 use 节点。节点成本见 06 性能,sprite 只省重复几何和 HTTP,不省 DOM。
5. 失败形态
| 症状 | 原因 |
|---|---|
| 图标空白 | 外部 use CORS / Safari;或 href 写成 xlink:href 且无 NS |
| 改 use 的 fill 不变色 | symbol 内部写死了 fill |
querySelector 找不到 path | shadow tree 封闭 |
display:none 的 sprite 实例尺寸为 0 | 被 none 的祖先连累,sprite 应从渲染树「看不见」但仍挂载 |
| 点选进了「内部 path」状态 | 没把 use 当原子,去穿透 shadow |
权威资料
核对日期:2026-08-26