前端性能监控怎么搞?我在裸辞半年后重新上岗的血泪实践
上个月刚结束半年的Gap期,重新回到上海的工位。说实话,这半年我睡得挺香,但一想到又要面对产品经理凌晨三点发来的“这个交互能不能再丝滑一点”的钉钉消息,心里还是有点发怵。不过嘛,生活总得继续,房贷也等不了——于是我火速入职了一家做SaaS工具的创业公司,坐标徐汇,离前司步行10分钟,房租没涨,算是一点小确幸。
入职第一天,CTO就扔给我一个任务:“我们前端性能太拉了,用户反馈页面卡成PPT,你搞个监控系统,下周上线。”
我当时内心OS:“大哥,我刚回来你就让我造火箭?” 但转念一想,这不正是我Gap期间刷GitHub、啃K8s、研究云原生时埋下的伏笔吗?行,干就完了。
为什么前端性能监控不是“可有可可无”?
先说背景。我们产品是个面向中小企业的低代码平台,前端用Vue3 + Vite,后端是Spring Boot微服务。用户在拖拽组件、配置流程时,如果页面响应慢超过2秒,流失率飙升30%——这是PM拿数据砸我脸时说的(感谢他,至少没甩锅)。
更惨的是,去年双11期间,因为某个第三方SDK加载超时,导致主应用白屏,客户投诉电话打爆了客服热线。运维兄弟半夜爬起来查日志,结果发现根本没打前端错误日志——前端异常就像幽灵,看不见摸不着,但用户能感受到。
于是,我意识到:没有监控的前端,就像开着没仪表盘的车,你以为在高速前进,其实可能已经爆胎了。
选型大战:自研 or 开源 or 商业?
我花了两天时间,在GitHub上狂翻项目,又对比了几家商业方案(比如Sentry、DataDog),最后列了个对比表:
| 方案 | 成本 | 上手难度 | 定制性 | 数据隐私 | 是否支持SPA |
|---|---|---|---|---|---|
| Sentry | $$$ | 低 | 中 | 依赖厂商 | ✅ |
| 自研(基于Beacon + Spring Boot) | $ | 高 | 高 | 完全可控 | ✅ |
| LogRocket | $$ | 低 | 低 | 第三方存储 | ✅ |
| Perfume.js + 自建后端 | $ | 中 | 高 | 可控 | ✅ |
考虑到公司预算紧张(创业公司懂的都懂),又不想把用户行为数据交给第三方,我决定走“轻量自研+开源轮子”路线。
核心思路:
- 用
PerformanceObserver和Navigation Timing API采集关键指标(FCP、LCP、FID) - 用
ErrorEvent捕获 JS 异常 - 通过
navigator.sendBeacon发送日志(避免页面卸载时丢数据) - 后端用 Spring Boot 接收并存入 ClickHouse(之前团队搭好的)
关键代码:别怕,真不难
1. 前端采集器(精简版)
// performance-monitor.js
class PerformanceMonitor {
constructor() {
this.init();
}
init() {
// 捕获JS错误
window.addEventListener('error', (e) => {
this.report({
type: 'js_error',
message: e.message,
stack: e.error?.stack || '',
url: window.location.href,
timestamp: Date.now()
});
});
// 捕获Promise rejection(很多人漏掉这个!)
window.addEventListener('unhandledrejection', (e) => {
this.report({
type: 'promise_rejection',
reason: e.reason?.toString() || 'Unknown',
timestamp: Date.now()
});
});
// 采集页面性能指标
if ('performance' in window) {
const perf = performance.getEntriesByType('navigation')[0];
if (perf) {
this.report({
type: 'navigation_timing',
fcp: this.getMetric('first-contentful-paint'),
lcp: this.getMetric('largest-contentful-paint'),
fid: this.getMetric('first-input-delay'), // 注意:FID需用Event Timing API
dns: perf.domainLookupEnd - perf.domainLookupStart,
tcp: perf.connectEnd - perf.connectStart,
ttfb: perf.responseStart - perf.requestStart,
load: perf.loadEventEnd - perf.loadEventStart
});
}
}
}
getMetric(name) {
// 使用PerformanceObserver监听特定指标
return new Promise(resolve => {
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === name) {
resolve(entry.startTime);
po.disconnect();
}
}
});
po.observe({ entryTypes: ['paint', 'largest-contentful-paint', 'first-input'] });
});
}
report(data) {
// 使用sendBeoncon确保页面关闭前也能发送
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
navigator.sendBeacon('/api/perf/report', blob);
}
}
// 启动监控
new PerformanceMonitor();
吐槽一句:FID(首次输入延迟)现在已经被INP(Interaction to Next Paint)取代了,但很多老项目还在用FID。如果你用的是Chrome 116+,建议直接上INP,不过得用
web-vitals库。
2. 后端接收(Spring Boot)
// PerfReportController.java
@RestController
public class PerfReportController {
@Autowired
private PerfService perfService;
@PostMapping("/api/perf/report")
public ResponseEntity<?> report(@RequestBody String payload) {
try {
JSONObject data = JSON.parseObject(payload);
// 简单校验
if (data.getString("type") == null) {
return ResponseEntity.badRequest().build();
}
perfService.save(data);
return ResponseEntity.ok().build();
} catch (Exception e) {
log.error("Failed to parse perf report", e);
return ResponseEntity.status(500).build();
}
}
}
这里有个坑:前端发的是Blob,Spring Boot默认用Jackson解析会失败。所以要么前端改发FormData,要么后端用@RequestBody String先接原始字符串再手动解析——我选了后者,省事。
工具链:让监控真正“活”起来
光有数据不够,得让人看得懂。我做了三件事:
- Grafana看板:把ClickHouse里的性能数据可视化,按页面、地域、设备类型分组。早上8点开工第一件事就是看昨天的LCP有没有超标。
- 企业微信告警:当某页面LCP > 4s 的比例超过5%,自动@我。上周五晚上11点就被@了,差点以为又要通宵——结果发现是测试环境域名配错了,虚惊一场。
- 本地开发插件:写了个Vite插件,在dev模式下实时显示当前页面的FCP/LCP,方便调优。再也不用开Chrome DevTools看Performance Tab了。
效果如何?数字不会骗人
上线一个月后,数据说话:
| 指标 | 优化前 | 优化后 | 降幅 |
|---|---|---|---|
| 平均LCP | 3.8s | 1.9s | 50% |
| JS错误率 | 2.1% | 0.4% | 76% |
| 页面完全加载时间 | 5.2s | 2.6s | 50% |
最爽的是,PM终于不再说“感觉卡”了,而是拿着Grafana截图问:“为什么A页面比B页面慢0.3秒?” —— 从玄学到科学,这就是监控的价值。
写在最后:代码人生,不止于写代码
这半年Gap,我一度怀疑自己是不是被技术淘汰了。但真正回归战场才发现,技术永远在变,但解决问题的思路是相通的。无论是K8s调度、Spring Boot优化,还是前端性能监控,核心都是:观测 → 分析 → 改进 → 验证。
另外,别迷信大厂方案。我在前司用的是整套DataDog + RUM,功能强大但贵到肉疼。现在这套自研方案,虽然简陋,但够用、可控、成本低——适合自己的,才是最好的。
如果你也在折腾前端监控,不妨去GitHub搜一下 web-vitals、perfume.js 这些开源库,站在巨人的肩膀上,少踩点坑。我的部分代码也打算脱敏后开源,等我忙完这波需求就传上去。
对了,今天又是8点起床干活的一天。窗外上海的雨下个不停,但看到LCP曲线一路下降,心情倒是晴朗得很。
毕竟,能让用户少等一秒,我们的代码人生,就多一分价值。

评论 0