搜索全站
主题 12 分钟阅读

WordPress 图标字体替换方案:用 SVG 精灵替代图标的完整做法

图标字体阻塞渲染、FOUT、无障碍差。本文讲清图标字体的四类问题,给出 SVG 精灵的构建、主题注入、PHP 辅助函数调用与缓存处理的完整代码,含从图标字体平滑迁移的步骤。

U
UFO_PF 2020-12-22 · 阅读 12 分钟
Wo 主题
阅读提示
本文约 4,593 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:为了几个小图标,加载一整包字体

很多主题为了显示搜索、菜单、日期、评论这些小图标,会引入一整套图标字体(dashicons、Font Awesome、theme icons)。代价是:一个几十 KB 的字体文件加一份 CSS,只为用到其中十几个字形。更难受的是字体加载完成前,页面上显示的是空白或方框——这就是 FOUT/FOIT。字体文件属于阻塞渲染资源,Lighthouse 会把它列进「排除阻塞渲染的资源」。

SVG 精灵(SVG sprite)是更合适的方案:把所有图标合并成一个 SVG 文件,页面里用 <use href="#icon-name"> 引用。它的优势是:可以用 currentColor 跟随文字颜色、支持多色图标、不依赖外部字体文件、不会因为字体加载失败而显示豆腐块、屏幕阅读器可通过 <title> 获得可读文本。本文给出从构建到主题集成的完整做法。若你只关心 dashicons 这一类 WordPress 自带的图标,可先看 主题加载的 dashicons 前台无用时的处理。

原理分析:图标字体到底差在哪

维度 图标字体 SVG 精灵
加载方式 外部字体文件 + CSS,阻塞渲染 内联或单个文件,可缓存复用
渲染稳定性 字体未到时显示空白/方框 随 HTML 一起到达,无闪烁
颜色控制 只能单色,靠 color 继承 currentColor 单色,也支持多色
无障碍 靠 ::before 伪元素,语义缺失 可用 <title> + aria-hidden
定位精度 受行高、基线影响,易错位 与普通元素一样参与盒模型

图标字体还有个隐性成本:伪元素依赖。字体图标通常写在 CSS 的 content: "\f002" 里,这类内容对屏幕阅读器不可见,对 SEO 无意义,也无法被用户选中。改成内联 SVG 后,可以用 aria-hidden="true" 明确标记为装饰性,或加 role="img" + <title> 表达语义。

精灵的实现方式有两种:内联到页面(把 <svg><symbol> 直接输出到 HTML,通常放在 wp_footer 或 wp_body_open)和外部文件引用(<use href="/assets/sprite.svg#icon-name">)。前者零额外请求、可跨域无碍,但无法被浏览器缓存复用;后者能被缓存,但旧版 Safari 对外部 use 引用支持不佳。综合看,内容站的图标数量通常不多,内联是更省事的选择。

保姆级实操

  1. 整理图标:确定站点真正用到的图标清单,别一股脑塞整套。
  2. 生成精灵文件:用构建工具(svgo + svg-sprite)或手工合并成 assets/svg/sprite.svg,每个图标包一层 <symbol id="icon-xxx" viewBox="0 0 24 24">。
  3. 去冗余属性:用 SVGO 去掉 fill 硬编码,改成 fill="currentColor",才能跟随文字颜色。
  4. 注入页面:在 wp_body_open 输出内联 <svg>,外层 hidden 或 display:none,但不能用 display:none 影响 <use>——实际上隐藏容器用 style="position:absolute;width:0;height:0;overflow:hidden" 最安全。
  5. 写辅助函数:提供 mimangfei_icon( $name, $args ),统一输出 <svg><use>。
  6. 替换模板:全局搜索主题的图标字体 class,逐个换成辅助函数。
  7. 清理旧资源:确认无残留后,注销图标字体的样式表。
# 安装构建依赖并生成精灵
npm i -D svgo svg-sprite
npx svgo -f src/icons --config=.svgo.config.js
npx svg-sprite -C --symbol --symbol-dest assets/svg --symbol-sprite sprite.svg "src/icons/*.svg"
<?php
// 子主题 functions.php:把精灵内联进页面
add_action( 'wp_body_open', 'mimangfei_output_svg_sprite', 1 );
function mimangfei_output_svg_sprite() {
    $path = get_stylesheet_directory() . '/assets/svg/sprite.svg';
    if ( ! file_exists( $path ) ) {
        return;
    }

    $svg = file_get_contents( $path );
    // 去掉 XML 声明与注释,避免重复输出头部
    $svg = preg_replace( '/<\?xml[^>]*\?>/i', '', $svg );
    $svg = preg_replace( '/<!--.*?-->/s', '', $svg );

    echo '<div class="svg-sprite" aria-hidden="true" '
       . 'style="position:absolute;width:0;height:0;overflow:hidden">' . $svg . '</div>';
}
<?php
// 统一图标输出函数:可选 title 提升无障碍
function mimangfei_icon( $name, $args = array() ) {
    $defaults = array(
        'class' => 'icon',
        'title' => '',
        'size'  => 24,
    );
    $args = wp_parse_args( $args, $defaults );

    $id    = 'icon-' . sanitize_html_class( $name );
    $class = esc_attr( $args['class'] . ' icon--' . sanitize_html_class( $name ) );
    $size  = absint( $args['size'] );

    $html = sprintf(
        '<svg class="%1$s" width="%2$d" height="%2$d" aria-hidden="%3$s" focusable="false">',
        $class,
        $size,
        $args['title'] ? 'false' : 'true'
    );

    if ( $args['title'] ) {
        $html .= '<title>' . esc_html( $args['title'] ) . '</title>';
    }

    $html .= '<use href="#' . esc_attr( $id ) . '"></use></svg>';
    return $html;
}
/* 图标基础样式:跟随文字颜色与行高 */
.icon {
    display: inline-block;
    vertical-align: -0.125em;
    fill: currentColor;
    flex: none;
}

.icon--search { color: var( --muted, #656d76 ); }

.svg-sprite symbol {
    /* 统一用 currentColor,删除源文件里的硬编码 fill */
    fill: currentColor;
}

常见踩坑

  1. display: none 隐藏精灵容器:部分浏览器下 <use> 引用 display:none 的 symbol 会渲染失败。用绝对定位 + 零尺寸的方式隐藏。
  2. SVG 里残留硬编码 fill="#000":导致 currentColor 无效,深色模式下图标变成黑块。必须用 SVGO 统一移除或改成 currentColor。
  3. 案例:某站替换完图标后,Chrome 正常、Safari 全部空白。原因是用了外部文件引用 <use href="/assets/sprite.svg#icon-search">,跨域与部分旧 Safari 版本对外部引用支持不稳定。改成内联精灵后所有浏览器正常。教训:图标数量在几十个以内时,优先内联,别为了省几 KB 引入兼容性风险。
  4. viewBox 缺失:从设计稿导出的 SVG 常没有 viewBox,导致无法按尺寸缩放。生成精灵时必须保留原始 viewBox。
  5. 图标 ID 冲突:主题和插件各输出一份精灵,同名 symbol 会互相覆盖。给自己的 symbol 加统一前缀,如 mmf-icon-。
  6. 忘记清理旧字体:图标换完了,字体 CSS 还在加载,纯属浪费。确认无残留引用后再 wp_dequeue_style()。
  7. 缓存与版本:内联精灵会随每个页面输出,改动后页面缓存不刷新会看到旧图标。精灵内容变化时要记得清全站缓存。
  8. 无障碍过度标记:装饰性图标应 aria-hidden="true";只有承担语义的图标才加 <title>。全加 title 会让屏幕阅读器读出大量噪音。

总结

是否值得从图标字体迁移,看两个条件:站点用到的图标是否少于 50 个(精灵体积可控),以及是否存在深色模式、多色图标、无障碍需求(图标字体难以满足)。行动清单:清点图标 → SVGO 清理并生成精灵 → wp_body_open 内联 → 写统一输出函数 → 逐个替换模板 → grep 确认无残留后注销字体样式 → 清缓存验证。迁移完成后,还可以顺手处理 主题深色模式(Dark Mode)实现方式,因为 SVG 图标用 currentColor 后会自动跟随深色配色,这是字体图标做不到的。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。