本文约 4,652 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:同样的服务器,换个主题慢一截
很多站长的体感是这样的:同一台机器、同一套插件,换了主题之后首屏明显变慢,PageSpeed 的”移除阻塞渲染的资源”和”确保文本在网页字体加载期间保持可见”两项一起飘红。于是开始装各种优化插件,插件叠插件,最后页面倒是快了一点,但后台越来越卡,偶尔还出现样式错乱。
其实主题层面的性能问题就那么几类:脚本堵在 <head> 里、静态资源带版本号导致缓存命中率低、字体没有 font-display、图片没压也没按需加载。这几件事在主题侧用不到三十行代码就能处理,比再装一个插件干净得多。
原理分析:阻塞渲染与缓存命中
浏览器解析 HTML 时,遇到 <head> 里没有 defer/async 的 <script> 会停下来下载并执行,这就是”阻塞渲染”。WordPress 的 wp_enqueue_script() 第五个参数 $in_footer 决定脚本输出在头部还是 wp_footer() 处;即便如此,第三方主题仍常把脚本放头部。
缓存命中率则与 URL 是否稳定有关。wp_enqueue_style() 默认给资源加上 ?ver=6.6.2 之类的查询串,对 CDN 与部分代理并不友好;同时每次 WordPress 升级都会改变版本号,导致全站 CSS/JS 缓存集体失效。更稳妥的做法是用文件的 filemtime() 作为版本——文件内容不变,版本号就不变。
字体方面,@font-face 缺 font-display: swap 时,浏览器在字体下载完成前不显示文字(FOIT),首屏看起来”卡住不动”。图片方面,loading="lazy" 与 srcset 已由核心支持,主题要做的通常是别把首屏图也设成懒加载。
| 优化项 | 主题侧做法 | 预期收益 |
|---|---|---|
| 脚本位置 | $in_footer + defer |
减少阻塞渲染 |
| 资源版本 | filemtime 代替核心版本 |
提高缓存命中 |
| 字体 | 本地化 + font-display: swap |
消除 FOIT、减少外部请求 |
| 图片 | 合理尺寸 + 首屏不懒加载 | 降低 LCP |
| 连接 | dns-prefetch / preconnect |
提前建连 |
保姆级实操
- 先测量再动手:用 PageSpeed Insights 或 GTmetrix 记录基线,重点看 LCP 与阻塞渲染资源清单。
- 把脚本挪到页脚:检查主题的
wp_enqueue_script()第五个参数;对第三方脚本用script_loader_tag追加defer。 - 去掉不必要的版本号:用
style_loader_src/script_loader_src过滤掉核心版本号。 - 用
filemtime管理版本:对自己主题的资源用文件修改时间做版本号,改动即更新,不改则稳定。 - 字体本地化:把 Web 字体放到主题目录,配
@font-face并加font-display: swap,同时用wp_resource_hints加预连接(详见字体本地化专题)。 - 首屏图不懒加载:把 LCP 图片的
loading属性设为eager并加fetchpriority="high"。 - 服务器侧补缓存头:静态资源加长
expires,HTML 不缓存或短缓存。
<?php
// 1) 给脚本加 defer(排除 jQuery,避免依赖报错)
add_filter( 'script_loader_tag', 'mimangfei_defer_scripts', 10, 2 );
function mimangfei_defer_scripts( $tag, $handle ) {
if ( is_admin() || 'jquery-core' === $handle ) {
return $tag;
}
if ( false !== strpos( $tag, ' defer' ) || false !== strpos( $tag, ' async' ) ) {
return $tag;
}
return str_replace( ' src=', ' defer src=', $tag );
}
// 2) 去掉核心版本号,改用文件修改时间
add_filter( 'style_loader_src', 'mimangfei_version_by_mtime', 20 );
add_filter( 'script_loader_src', 'mimangfei_version_by_mtime', 20 );
function mimangfei_version_by_mtime( $src ) {
if ( $src && false !== strpos( $src, 'ver=' ) ) {
$src = remove_query_arg( 'ver', $src );
}
return $src;
}
add_filter( 'wp_enqueue_scripts', 'mimangfei_enqueue_assets', 5 );
function mimangfei_enqueue_assets() {
$css = get_stylesheet_directory() . '/assets/css/main.css';
wp_enqueue_style(
'mimangfei-main',
get_stylesheet_directory_uri() . '/assets/css/main.css',
array(),
file_exists( $css ) ? filemtime( $css ) : null
);
}
<?php
// 3) 首屏主图不做懒加载,并提升加载优先级
add_filter( 'wp_get_attachment_image_attributes', 'mimangfei_lcp_image', 10, 2 );
function mimangfei_lcp_image( $attr, $attachment ) {
if ( is_front_page() && ! empty( $attr['class'] ) && false !== strpos( $attr['class'], 'hero-image' ) ) {
$attr['loading'] = 'eager';
$attr['fetchpriority'] = 'high';
}
return $attr;
}
// 4) 提前与资源域名建连
add_filter( 'wp_resource_hints', 'mimangfei_resource_hints', 10, 2 );
function mimangfei_resource_hints( $hints, $relation_type ) {
if ( 'preconnect' === $relation_type ) {
$hints[] = array(
'href' => 'https://cdn.example.com',
'crossorigin' => '',
);
}
return $hints;
}
/* 5) 字体本地化 + font-display,避免文字短暂消失 */
@font-face {
font-family: "Inter";
src: url("../fonts/inter-var.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
:root {
--font-body: "Inter", system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: var(--font-body);
}
# 6) 静态资源长缓存,HTML 不缓存
location ~* \.(?:css|js|woff2?|ttf|svg|webp|avif|png|jpe?g|gif)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$args;
add_header Cache-Control "no-store, must-revalidate";
}
常见踩坑
- 一股脑给所有脚本加
defer,jQuery 插件在jQuery未就绪时执行,控制台满屏$ is not a function。 - 去掉版本号后,改了 CSS 但 CDN 还缓存着旧文件,用户看到错版——这就是为什么要用
filemtime而不是彻底去掉版本。 - 字体本地化了但忘了
font-display,FOIT 依旧存在。 - 首屏主图被懒加载,LCP 反而更差。
- 案例:某站为了”减少请求”把所有 JS 合并并加
async,结果轮播脚本先于 jQuery 执行,首页轮播白屏两天。最后按依赖拆分:jQuery 保持同步,业务脚本统一defer,问题消失。 preconnect加了太多域名,反而占用连接资源。- 服务器侧给 HTML 也加了长缓存,用户改文章后看到的仍是旧页面。
总结
主题侧性能优化按”脚本位置 → 资源版本 → 字体 → 图片 → 连接”的顺序做,每做一项测一次,避免一次性改动无法定位。判断标准是:LCP 元素不被懒加载、首屏无阻塞脚本、字体有 font-display、静态资源带长缓存头。行动清单:① 记录基线;② 只给非依赖脚本加 defer;③ 版本号用 filemtime;④ 首屏图 eager + fetchpriority;⑤ HTML 与静态资源分开设置缓存头。延伸阅读 主题字体本地化、WordPress 缓存插件设置 与 lumen-mimangfei 主题自定义 CSS 写法。
