Product Updates Statnive 插件 · Parhum Khoshbakht

我们如何打造低性能开销的 Statnive

三项架构改动 — 异步加载、内联核心 tracker 和空闲回调 — 把 Statnive 的基准 LCP 影响砍掉了一半。这是工程故事和诚实的注意事项。

在我们的测试中从最后一名到开销最低

当我们第一次把 Statnive 与其他 7 款 WordPress 分析插件做基准对比时,结果让我们很难为情。我们的 TTFB 很出色 — 第 4 快。但 Largest Contentful Paint 在自托管插件中垫底。服务器响应到访客真正看到内容之间的差距是 202 毫秒。Koko Analytics 是 94ms。Burst Statistics 是 80ms。我们是 202ms。

问题不在 tracker 代码本身。在于 WordPress 加载它的方式。

到当天结束时,我们已经把这个差距压缩到了 79 毫秒。LCP 从 504ms 降到 288ms — 提升 43%。在轻负载下,我们与第二名持平。在后续的合成压力测试中(50 个并发 HTTP 用户、无页面缓存),Statnive 在我们测量的 8 个插件里 LCP 开销最低。下面就是我们改了什么、为什么 — 以及关于这些基准数字真实意义的诚实注意事项。

基准测试:8 个插件、真实 Chromium、真实负载

我们搭建了一个自动化测试框架,通过 WordPress REST API 切换分析插件,使用 k6 跑真实的 Chromium 浏览器访问,并通过 PerformanceObserver 收集 Core Web Vitals。每个插件都在完全隔离的环境下运行 — 所有其他分析插件停用、缓存清空、在测量开始前用 5 个请求预热服务器。

前后对比结果:

指标之前之后变化
Statnive TTFB294ms209ms-29%
Statnive FCP496ms288ms-42%
Statnive LCP504ms288ms-43%
TTFB 到 LCP 的差距202ms79ms-61%
排名(LCP)第 7/8第 2(并列)+5 名

根因:三个性能杀手

我们把 202ms 的差距追溯到 FrontendHandler.php 中的三个问题,每一个都由 WordPress Core 文档和网页性能研究独立证实。

问题 1:wp_localize_script 强制阻塞模式。 WordPress 6.3 通过 strategy 参数引入了原生的 async/defer 支持。但 wp_localize_script() 会在「after」位置生成一段内联脚本 — 根据 WordPress Core Trac #58632 — 这会强制父脚本进入阻塞模式,并沿整个依赖树向下级联。链上的每一个脚本都会失去其 async/defer 策略。

问题 2:没有 asyncdefer 属性。 我们的 tracker 是用 ['in_footer' => true] 入队的,但没有 strategy 参数。即便在页脚,同步脚本也会阻止浏览器在下载和执行完成前触发 load 事件。

问题 3:每次页面加载都计算 SRI 散列。 我们在每一个页面请求上调用 file_get_contents() + hash('sha256', ...) 来生成 Subresource Integrity 散列。这是每一位访客都要执行一次的文件读取加 CPU 密集型散列。

获取 Statnive:性能优先的自托管分析

本文中描述的所有优化今天都已随 Statnive 一起交付。从 WordPress.org 免费安装 — 您的数据留在您的服务器,您的页面保持快速。

阶段 1:修复加载策略

最大的单项收益来自对 FrontendHandler.php 的三处改动:

wp_add_inline_script('before') 替换 wp_localize_script 'before' 位置是关键 — 它不会级联进入阻塞模式。'after' 位置(默认值)会级联。这一区别记录在官方的 WordPress 6.3 脚本加载公告中,但很容易被忽略。

// Before (forces blocking):
wp_localize_script( 'statnive-tracker', 'StatniveConfig', $config );

// After (safe with async):
wp_add_inline_script(
    'statnive-tracker',
    'window.StatniveConfig=' . wp_json_encode( $config ) . ';',
    'before'  // MUST be 'before' — 'after' cascades to blocking
);

wp_enqueue_script 加上 strategy: 'async' 对于不需要访问 DOM 的分析 tracker 而言,asyncdefer 更好。Defer 要等完整的 HTML 解析(在复杂页面上要 500ms+)。Async 在下载完成时立即执行。我们的 tracker 读取 window.StatniveConfig 并触发 navigator.sendBeacon() — 都不需要 DOM。

把 SRI 散列缓存到一个 WordPress transient 中。filemtime() 作为键,散列计算一次后被复用,直到文件变更。新构建 = 新的修改时间 = 缓存自动失效。

阶段 2:解放主线程

异步加载就位后,我们把目光转向 tracker 的 JavaScript 本身。

移除 DOMContentLoaded 包装。async 模式下,脚本一下载完就执行。tracker 读取的是 windownavigator 全局对象 — 不需要 DOM。DOMContentLoaded 事件监听器只是徒增延迟。

通过 requestIdleCallback 延迟非关键模块。 页面浏览这一次上报是唯一的关键路径操作。互动追踪(滚动深度、停留时间)、自动追踪(外部链接、表单提交)以及 CSS 事件追踪都可以等浏览器空闲再来。Safari 自 2024 年 9 月起原生支持 requestIdleCallback,因此在现代浏览器中无需 polyfill。

// Critical path: fires immediately
sendHit(buildPayload());

// Deferred: runs when browser is idle
var idle = window.requestIdleCallback || function(cb) { setTimeout(cb, 80); };
idle(function() {
    engagementTracker.start();
    registerAutoTracking(sendEvent);
});

我们研究中的关键洞察:不要requestIdleCallbacktimeout 参数。timeout 会在用户交互期间也强制执行,可能导致卡顿并伤害 INP 评分。让浏览器决定它真正空闲的时机。

阶段 3:消除外部请求

最后一项优化彻底将外部脚本下载从关键渲染路径中剔除。受到 Google 的 gtag.js 使用基于队列的内联引导以及 Koko Analytics 把整个 468 字节 tracker 内联进页面这两种方式启发,我们设计了一个两阶段架构。

阶段 1:内联核心 tracker(1.1KB)。 一个最小的 IIFE,读取配置、检查隐私信号(DNT/GPC)、运行 4 项机器人检测启发式、构造页面浏览负载,并通过 navigator.sendBeacon() 发送。它通过 wp_print_inline_script_tag()wp_footer 中直接打印进 HTML。零外部请求。

阶段 2:异步完整 tracker(5KB)。 包含互动、事件、自动追踪和同意管理的完整 tracker 以 strategy: 'async' 加载。在初始化时,它会检查 window.statnive_hit_sent — 如果内联核心已经发送过页面浏览,就直接跳到延迟模块的初始化。不会重复上报。

结果:页面浏览在任何外部资源完成加载前,由内联 JavaScript 触发。完整功能集在后台加载,不影响任何 Core Web Vital。

各阶段结果

每个阶段都被独立部署并测量:

阶段改动差距LCP
优化前阻塞脚本,无策略202ms504ms
阶段 1:async + 内联配置非阻塞下载约 80ms约 374ms
阶段 2:requestIdleCallback解放主线程约 65ms约 359ms
阶段 3:内联核心 tracker零外部请求79ms288ms

合成压力测试:架构在负载下的表现

轻负载会掩盖架构差异。为了对全部 8 个插件做压力测试,我们重新跑了基准:10 个 Chromium 浏览器用户测量 Core Web Vitals,同时 50 个并发 HTTP 用户狂轰服务器。没有安装任何页面缓存插件。每个请求都跑完整的 WordPress PHP 路径 — 这是一种刻意制造的病态条件,用于揭示哪些插件在竞争下会退化。

结果 — 在我们单次压力测试中相对基线的 LCP 开销,每个插件约 150 个样本:

排名插件LCP Δ影响分
1Statnive+260ms6.7
2Independent Analytics+566ms14.2
3Jetpack+776ms19.5
4MonsterInsights (GA4)+964ms24.1
5WP Slimstat+1030ms25.4
6WP Statistics+1424ms35.9
7Koko Analytics+2278ms56.3
8Burst Statistics+3592ms89.6

这些不是生产环境数字。 它们来自一台无缓存的开发者机器上的单次运行。一个使用 W3TC、WP Rocket 或 CDN 页面缓存的生产 WordPress 站点会显示出小得多的差异,因为缓存页面根本不会执行分析插件的 PHP 代码。Koko Analytics 和 Burst Statistics 的较大 LCP 差,很可能反映的是测试特定的竞争问题(WP-Cron 批处理、数据库写入串行化),而不是真实站点上的稳态开销。

这次测试确实展现的是:Statnive 的架构无论服务器端有多少竞争,都能保持关键渲染路径畅通:内联核心在任何服务器工作开始前就触发了 navigator.sendBeacon(),因此即使数据库负载很重,页面浏览也会被捕获。架构上的胜利才是故事 — 不是那些具体的倍数。在您自己的硬件上运行测试,再对您的具体配置下结论。

基于研究的决策

每一个技术决策都对照已发表的研究和官方文档进行了验证。我们查阅了 100 多份资料,涵盖 WordPress Core Trac 工单、web.dev 性能指南、W3C 规范以及生产可靠性研究。塑造我们方法的关键发现:

  • wp_add_inline_script('before') 被官方明确记录为可与 async/defer 策略安全配合(Make WordPress Core,2023 年 7 月)
  • 通过 createElement 注入脚本比原生 <script async> 慢 2.1 秒,因为它绕过了浏览器的预加载扫描器(Ilya Grigorik,Google)
  • 当与 visibilitychangepagehide 事件配合时,navigator.sendBeacon() 的传送可靠性可达 95.8–98%(NicJ.net 生产研究,200 万+ 页面浏览)
  • 移动端的 JavaScript 解析/编译比桌面慢 2–5 倍,但我们 5KB 的 tracker 远低于需要拆分的 50KB 阈值(Addy Osmani,Google)

常见问题

内联核心 tracker 与内容安全策略兼容吗?

兼容。wp_print_inline_script_tag() 会遵循 WordPress 的 wp_inline_script_attributes 过滤器,可以为 CSP 合规添加 nonce。该内联脚本是服务器端生成的,不包含任何用户输入。

如果异步完整 tracker 加载失败会怎样?

页面浏览已经被内联核心记录了。该会话的互动和事件追踪会丢失,但核心分析数据已被捕获。这是优雅降级 — 最重要的指标(页面浏览)拥有最可靠的传送保障。

完整 tracker 为什么是 async 而不是 defer?

Defer 要等完整的 HTML 解析后才执行。对于不操作 DOM 的分析 tracker 来说,这是没必要的延迟。Async 并行下载并立即执行。内联的 'before' 脚本保证了 StatniveConfig 在异步脚本运行前就已可用。

这种方法在 6.3 之前的 WordPress 版本上能用吗?

strategy 参数需要 WordPress 6.3+。在更老的版本上,该参数会被静默忽略,脚本会作为标准页脚脚本加载 — 仍可工作,只是没有 async 优化。Statnive 要求 WordPress 6.4+。

接下来呢

我们的 tracker 在合成压力测试中拿到了第一名,但单次基准并不等同于生产验证。下一步要研究的领域:

  • 多次运行基准并报告方差:以随机配置顺序运行重负载测试 5 次,并报告中位数加四分位距,而不是单次中位数
  • 启用页面缓存的基准:在 W3TC 和 WP Rocket 一起使用的情况下测试所有插件,展示在真实生产配置下的对比是什么样
  • 独立验证:整个框架是开源的 — 我们非常希望有第三方运行它并发布自己的结果
  • 编译期功能变体(Plausible 的模式):基于启用的功能生成不同的 tracker 构建产物,让不使用互动追踪的网站获得更小的脚本
  • Service Worker 持久化:用 service worker 排队事件,即使在不稳定的移动网络下也能可靠传送
  • 服务器端 TTFB 降低:分析 PHP 上报端点,从服务器响应中再省下若干毫秒

性能不是您一次性交付的功能。它是您在每次发布中践行的纪律 — 而诚实测量是这种纪律的一部分。

看看 Statnive 的性能与 Google AnalyticsMonsterInsights 以及其他 WordPress 分析插件的对比。或者探索全部 Statnive 功能

免费获取 Statnive