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 本身没动这段 JSON,但 WordPress 的 update_metadata 函数内部会调用 wp_unslash(),把转义过的引号全部恢复成原始引号,导致 JSON 里出现未转义的双引号,解析直接失败。

更准确地说:$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-settingsbody-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%。

发表回复

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

或许还会想看: