WordPress Elementor 字体替换避坑指南:从5种字体到2种,我踩过的5个坑

WordPress Elementor 字体替换避坑指南:从 5 种字体到 2 种,我踩过的 5 个坑

最近为一个香薰品牌官网做了一次字体整合优化,目标是把全站 5 种 Google 字体(Oswald、Maitree、Nunito、Source Sans Pro、Hind)统一为 2 种(Playfair Display + Raleway),并全部托管到本地服务器,彻底摆脱对 Google Fonts CDN 的依赖。

听起来很简单?无非是在 Elementor 里把字体选择器改一改。但当真正动手时,我发现事情远没那么简单——Elementor 把字体信息散布在至少 4 个互不相干的地方,每处都有自己的存储方式和陷阱。

这篇文章记录了我走的弯路和最终方案,希望后来者能跳过这些坑。

现象:一个”简单”的字体替换

站点使用的是 Elementor + Astra 主题 + Perfmatters 插件。访问首页,查看网络请求,能看到多个 fonts.googleapis.comfonts.gstatic.com 的请求。

Elementor 会在 wp-content/uploads/elementor/google-fonts/ 目录下下载字体文件,累计约 2.5MB。同时 Perfmatters 也会尝试本地缓存字体,两者互有重叠。

听起来像一个标准的字体优化任务:改字体引用、启用本地托管、清掉外部请求,就完成了。但我很快发现,这个”简单”任务的复杂度远超出预期。

排查过程:字体信息的 4 个藏身处

我在站点上运行了全面的字体审计脚本,遍历所有 Elementor 页面、模板、Kit 全局设置、Astra 主题设置、Perfmatters 选项。

第一个发现:Elementor 数据有两层

Elementor 的字体信息主要存在两个地方。

第一层是每个页面的 _elementor_data,一个 JSON 字符串,存储了页面中每个部件(Widget)的样式设置。字体设置在类似这样的路径下:


_elementor_data → elements[].elements[].settings.typography_font_family

第二层是 Elementor Kit 的全局排版预设_elementor_page_settings),定义了 4 个 Scheme:Primary/Secondary/Text/Accent,以及按钮排版。这些预设值通过 globals/typography?id=xxx 被各个页面引用。

这两个地方都要改,缺一不可。如果只改页面的 _elementor_data 而不管 Kit,新增页面会继续使用旧字体。

第二个发现:还有两个我没注意到的字体来源

  1. Astra 主题的全局字体设置存储在 astra-settings 选项中,是一个序列化的 PHP 数组。其中 body-font-family 引用了 Hind,这个设置控制了所有不在 Elementor 范围内的页面元素的字体。
  2. Perfmatters 插件的 local_google_fonts 设置控制是否启用本地字体缓存。数据库恢复后这个设置会被回滚,需要重新启用。

这 4 个字体来源必须一起处理,否则总会有一个地方残留旧字体。

第一个坑:str_replace 毁掉了 Elementor 的 JSON 数据

自以为最稳妥的做法是直接在数据库里用 str_replace 把旧字体名字替换成新字体。毕竟这是教科书式的操作,对吧?


wp db query "UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'Oswald', 'Playfair Display') WHERE meta_key = '_elementor_data'"

执行后,我刷新了缓存,打开页面——一片空白。控制台报 JSON 解析错误。

排查发现,_elementor_data 中有一部分元素的 __dynamic__ 字段长这样:


"__dynamic__": {
    "caption": "[elementor-tag id=\"95e6888\" name=\"post-custom-field\" settings=\"%7B%7D\"]"
}

问题就在这里。elementor-tag 里的 id="95e6888" 包含了带引号的属性值。str_replace 在替换 Oswald 的过程中,虽然没有动到这段 JSON,但 WordPress 的 update_metadata 函数内部会调用 wp_unslash(),把转义过的引号全部恢复成原始引号,导致 JSON 出现未转义的双引号,解析失败。

更准确的解释是:WordPress 的 $wpdb->update() 不会自动序列化/反序列化数据。当我用 str_replace 直接对数据库操作时,跳过了 WordPress 的 meta 处理层,而 Elementor 的数据在写入时经过了序列化处理(maybe_serialize)。str_replace 破坏了序列化的键名结构。

教训:永远不要对 WordPress 的 _elementor_data 字段使用 str_replace。正确的做法是:


// 正确的做法:通过 WP API 读取、修改、写入
$post_id = 1104;
$data = get_post_meta($post_id, '_elementor_data', true);
$data = json_decode($data, true);
// ... 遍历修改字体 ...
update_post_meta($post_id, '_elementor_data', wp_slash(json_encode($data)));

或者更稳妥的做法是写一个遍历脚本,对每个帖子读出来、改好、再写回去,避免直接在数据库层面操作。

第二个坑:两个”Default Kit”,我改了错的那个

审计发现数据库里有两个名为 “Default Kit” 的帖子,ID 分别是 5 和 669。


SELECT ID, post_title, post_status FROM wp_posts 
WHERE post_type = 'elementor_library' AND post_name LIKE '%kit%';

输出:


+-----+-------------+-------------+
| ID  | post_title  | post_status |
+-----+-------------+-------------+
| 669 | Default Kit | publish     |
|   5 | Default Kit | publish     |
+-----+-------------+-------------+

哪个才是真正起作用的?检查 elementor_active_kit 选项:


wp option get elementor_active_kit
# 输出:669

活动的 Kit 是 ID 669,而我第一次修复时操作了 ID 5(因为它是第一个找到的 “Default Kit”)。

更戏剧性的是,当我检查 Kit 669 的 _elementor_page_settings 时,发现它的 system_typography 已经正确设置为了 Playfair Display + Raleway。也就是说,第一轮最关键的修改之一——更新全局排版预设——实际上是白费功夫。

这引出了一个有趣的问题:为什么 Kit 669 已经是对的,但页面还在用旧字体?因为 Kit 的 Scheme 引用的字体只是”默认值”,而 Elementor 每个页面的具体部件在保存时会把字体值固化到 _elementor_data,不再跟随 Kit 的变化。所以即使 Kit 改对了,已有的页面仍然显示旧字体——需要逐页修改 _elementor_data

教训

  • elementor_active_kit 确认哪个 Kit 是活动的,不要猜
  • Kit 修改只影响新创建的页面,已有页面的字体需要逐个修改 _elementor_data
  • Elementor 的 system_schemes[typography](弃用)和 system_typography 是两个不同的 meta key,后者才是当前版本使用的

第三个坑:Starter Templates 残留的 Astra 字体

在完成所有 Elementor 页面的替换并刷新缓存后,我检查了 Perfmatters 生成的字体缓存文件——里面竟然还有 Hind 的引用。

通过全局搜索 wp_options 表,我发现在 astra-settings 中,body-font-family 的值是:


'body-font-family' => 'Hind', sans-serif

这原本是网站使用 Astra Starter Templates 导入演示站点时残留的字体设置。虽然 Astra 主题允许在自定义器中修改字体,但这个旧值仍然保留在数据库里,会被 Elementor 之外的页面区域(比如页面未用 Elementor 渲染的部分)使用。

同时,还有 3 条 astra_sites_body_font_family 元数据也保留着 Hind,这是 Starter Templates 插件的历史记录。

教训:Elementor 字体替换完成后,一定要检查 Astra 主题的 body-font-family。如果使用过 Starter Templates,也要清理相关元数据残留。这些不在 Elementor 管理范围内的字体很容易被遗漏。

第四个坑:Elementor 和 Perfmatters 的字体加载冲突

完成所有替换后,我检查了页面的网络请求,确认没有 fonts.googleapis.com 的外部请求。但发现了一些 404 请求,指向 Elementor 本地的旧字体 CSS 文件。

原因:Elementor 有一个实验性功能 e_local_google_fonts(在 Elementor 实验设置中),它的作用是额外生成一份字体 CSS 文件。但同时 Perfmatters 的 local_google_fonts 也在做同样的事情。两者并存时:

  1. Perfmatters 负责拦截对 Google Fonts CDN 的请求并替换为本地副本
  2. Elementor 也在自己的目录下生成字体文件
  3. 旧字体(Oswald、Maitree 等)的 CSS 引用还在 Elementor 的缓存表中,但对应的字体文件已经被删除

结果就是 404。而且两个插件各管各的,加载了重复的字体 CSS。

解决方案是二选一:我选择了保留 Perfmatters 的本地字体(因为它更全局,对主题和非 Elementor 内容也生效),关闭 Elementor 自带的本地字体功能。


wp option update elementor_local_google_fonts 0
wp option update elementor_experiment-e_local_google_fonts inactive

教训:两个插件做同一件事,反而会互相干扰。选择一个作为字体加载的主渠道,关闭另一个。

第五个坑:字体换了,字号和行高不一定合适

更改完所有字体后,我发现一些视觉上的不协调。

Oswald 是一种紧凑的粗体无衬线体,视觉占比大。换成 Playfair Display(一种衬线体)后,同样的 26px 感觉更”轻盈”了,但有些地方反而不够突出。Raleway 替换 Nunito 后,正文阅读体验需要重新评估。

重点问题有两个:

Text 行高问题。全局预设中 Text 的行高是 18px(字号 15px),比例仅为 1.2。这个比例对衬线体来说过于紧凑,对无衬线体来说也偏低。推荐 1.5 倍以上(22-24px)。

按钮字号问题。Kit 按钮排版使用了 26px Raleway Bold,这在实际渲染中显得很大。Raleway Bold 的字形比 Oswald Bold 宽,26px 的按钮文字在手机端几乎占满整个按钮。建议 14-18px。

这些不算是”错误”,而是字体替换后的视觉适配工作。不同字体的字符宽度、x-height、笔画粗细都不同,同一点数下的视觉效果差别很大。

总结

这次字体替换是一次典型的”看似简单实则复杂”的任务。以下是经过验证的操作流程,供参考:

  1. 完整审计:遍历所有 Elementor 页面、Kit、Astra 主题设置、插件设置,找到所有字体引用
  2. 使用 WP API 修改:永远不要用 str_replace 直接操作 _elementor_data,用 get_post_metajson_decode → 修改 → wp_slash(json_encode())update_post_meta
  3. 确认活动 Kit:使用 wp option get elementor_active_kit 确认哪个 Kit 是活动的
  4. 检查 Astra 和其他主题设置:Elementor 之外的字体来源很容易被遗漏
  5. 选择字体加载主渠道:Perfmatters 还是 Elementor 自带的本地字体,二选一
  6. 逐页更新:Kit 修改只影响新增页面,已有页面需要用脚本逐个更新
  7. 视觉验证:替换后检查各页面的实际渲染效果,特别是行高和字号
  8. 清理残留:删除旧字体文件、清除各层缓存(Elementor CSS、WordPress、CDN/LiteSpeed)
  9. 复查网络请求:确认没有外部字体 CDN 请求,没有 404

相关数据:全站 15 个页面/模板,120 处 Elementor 级替换,1 处 Astra 全局字体修正,文件体积从 2.5MB 降到约 380KB,降幅 85%。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

或许还会想看: