omgkit 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugin/skills/databases/mongodb/SKILL.md +81 -28
- package/plugin/skills/databases/prisma/SKILL.md +87 -32
- package/plugin/skills/databases/redis/SKILL.md +80 -27
- package/plugin/skills/devops/aws/SKILL.md +80 -26
- package/plugin/skills/devops/github-actions/SKILL.md +84 -32
- package/plugin/skills/devops/kubernetes/SKILL.md +94 -32
- package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
- package/plugin/skills/frameworks/django/SKILL.md +158 -24
- package/plugin/skills/frameworks/express/SKILL.md +153 -33
- package/plugin/skills/frameworks/fastapi/SKILL.md +153 -34
- package/plugin/skills/frameworks/laravel/SKILL.md +146 -33
- package/plugin/skills/frameworks/nestjs/SKILL.md +137 -25
- package/plugin/skills/frameworks/rails/SKILL.md +594 -28
- package/plugin/skills/frameworks/react/SKILL.md +94 -962
- package/plugin/skills/frameworks/spring/SKILL.md +528 -35
- package/plugin/skills/frameworks/vue/SKILL.md +147 -25
- package/plugin/skills/frontend/accessibility/SKILL.md +145 -36
- package/plugin/skills/frontend/frontend-design/SKILL.md +114 -29
- package/plugin/skills/frontend/responsive/SKILL.md +131 -28
- package/plugin/skills/frontend/shadcn-ui/SKILL.md +133 -43
- package/plugin/skills/frontend/tailwindcss/SKILL.md +105 -37
- package/plugin/skills/frontend/threejs/SKILL.md +110 -35
- package/plugin/skills/languages/javascript/SKILL.md +195 -34
- package/plugin/skills/methodology/brainstorming/SKILL.md +98 -30
- package/plugin/skills/methodology/defense-in-depth/SKILL.md +83 -37
- package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +92 -31
- package/plugin/skills/methodology/executing-plans/SKILL.md +117 -28
- package/plugin/skills/methodology/finishing-development-branch/SKILL.md +111 -32
- package/plugin/skills/methodology/problem-solving/SKILL.md +65 -311
- package/plugin/skills/methodology/receiving-code-review/SKILL.md +76 -27
- package/plugin/skills/methodology/requesting-code-review/SKILL.md +93 -22
- package/plugin/skills/methodology/root-cause-tracing/SKILL.md +75 -40
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +75 -224
- package/plugin/skills/methodology/systematic-debugging/SKILL.md +81 -35
- package/plugin/skills/methodology/test-driven-development/SKILL.md +120 -26
- package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +88 -35
- package/plugin/skills/methodology/token-optimization/SKILL.md +73 -34
- package/plugin/skills/methodology/verification-before-completion/SKILL.md +128 -28
- package/plugin/skills/methodology/writing-plans/SKILL.md +105 -20
- package/plugin/skills/omega/omega-architecture/SKILL.md +178 -40
- package/plugin/skills/omega/omega-coding/SKILL.md +247 -41
- package/plugin/skills/omega/omega-sprint/SKILL.md +208 -46
- package/plugin/skills/omega/omega-testing/SKILL.md +253 -42
- package/plugin/skills/omega/omega-thinking/SKILL.md +263 -51
- package/plugin/skills/security/better-auth/SKILL.md +83 -34
- package/plugin/skills/security/oauth/SKILL.md +118 -35
- package/plugin/skills/security/owasp/SKILL.md +112 -35
- package/plugin/skills/testing/playwright/SKILL.md +141 -38
- package/plugin/skills/testing/pytest/SKILL.md +137 -38
- package/plugin/skills/testing/vitest/SKILL.md +124 -39
- package/plugin/skills/tools/document-processing/SKILL.md +111 -838
- package/plugin/skills/tools/image-processing/SKILL.md +126 -659
- package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
- package/plugin/skills/tools/media-processing/SKILL.md +118 -735
- package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
|
@@ -1,905 +1,101 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: performance
|
|
3
|
-
description:
|
|
2
|
+
name: profiling-performance
|
|
3
|
+
description: Performs browser performance profiling with Lighthouse, Core Web Vitals, and DevTools analysis. Use when auditing page performance, optimizing Core Web Vitals, analyzing bundle sizes, or implementing performance budgets.
|
|
4
4
|
category: devops
|
|
5
5
|
triggers:
|
|
6
|
-
- performance
|
|
7
|
-
- lighthouse
|
|
6
|
+
- performance
|
|
7
|
+
- lighthouse
|
|
8
8
|
- core web vitals
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- performance optimization
|
|
12
|
-
- bundle analysis
|
|
9
|
+
- bundle size
|
|
10
|
+
- profiling
|
|
13
11
|
---
|
|
14
12
|
|
|
15
|
-
# Performance
|
|
13
|
+
# Profiling Performance
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Purpose
|
|
20
|
-
|
|
21
|
-
Performance directly impacts user experience and business metrics:
|
|
22
|
-
|
|
23
|
-
- Run automated Lighthouse audits with detailed analysis
|
|
24
|
-
- Monitor Core Web Vitals (LCP, FID, CLS, INP)
|
|
25
|
-
- Identify JavaScript performance bottlenecks
|
|
26
|
-
- Analyze network waterfalls and resource loading
|
|
27
|
-
- Detect memory leaks and optimize memory usage
|
|
28
|
-
- Create performance budgets and regression testing
|
|
29
|
-
|
|
30
|
-
## Features
|
|
31
|
-
|
|
32
|
-
### 1. Lighthouse Automation
|
|
15
|
+
## Quick Start
|
|
33
16
|
|
|
34
17
|
```typescript
|
|
18
|
+
// Run Lighthouse audit
|
|
35
19
|
import lighthouse from 'lighthouse';
|
|
36
20
|
import * as chromeLauncher from 'chrome-launcher';
|
|
37
21
|
|
|
38
|
-
|
|
39
|
-
url: string;
|
|
40
|
-
categories?: ('performance' | 'accessibility' | 'best-practices' | 'seo')[];
|
|
41
|
-
device?: 'mobile' | 'desktop';
|
|
42
|
-
throttling?: ThrottlingConfig;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface LighthouseResult {
|
|
46
|
-
scores: Record<string, number>;
|
|
47
|
-
metrics: PerformanceMetrics;
|
|
48
|
-
opportunities: Opportunity[];
|
|
49
|
-
diagnostics: Diagnostic[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Run Lighthouse audit
|
|
53
|
-
async function runLighthouseAudit(config: LighthouseConfig): Promise<LighthouseResult> {
|
|
22
|
+
async function audit(url: string) {
|
|
54
23
|
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
port: chrome.port,
|
|
59
|
-
output: 'json',
|
|
60
|
-
onlyCategories: config.categories || ['performance'],
|
|
61
|
-
formFactor: config.device || 'mobile',
|
|
62
|
-
throttling: config.throttling || {
|
|
63
|
-
rttMs: 150,
|
|
64
|
-
throughputKbps: 1638,
|
|
65
|
-
cpuSlowdownMultiplier: 4,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const result = await lighthouse(config.url, options);
|
|
70
|
-
const lhr = result?.lhr;
|
|
71
|
-
|
|
72
|
-
if (!lhr) throw new Error('Lighthouse failed to generate report');
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
scores: {
|
|
76
|
-
performance: lhr.categories.performance?.score ?? 0,
|
|
77
|
-
accessibility: lhr.categories.accessibility?.score ?? 0,
|
|
78
|
-
bestPractices: lhr.categories['best-practices']?.score ?? 0,
|
|
79
|
-
seo: lhr.categories.seo?.score ?? 0,
|
|
80
|
-
},
|
|
81
|
-
metrics: extractMetrics(lhr),
|
|
82
|
-
opportunities: extractOpportunities(lhr),
|
|
83
|
-
diagnostics: extractDiagnostics(lhr),
|
|
84
|
-
};
|
|
85
|
-
} finally {
|
|
86
|
-
await chrome.kill();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Extract key metrics
|
|
91
|
-
function extractMetrics(lhr: LH.Result): PerformanceMetrics {
|
|
92
|
-
const audits = lhr.audits;
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
// Core Web Vitals
|
|
96
|
-
lcp: {
|
|
97
|
-
value: audits['largest-contentful-paint']?.numericValue ?? 0,
|
|
98
|
-
score: audits['largest-contentful-paint']?.score ?? 0,
|
|
99
|
-
displayValue: audits['largest-contentful-paint']?.displayValue ?? '',
|
|
100
|
-
},
|
|
101
|
-
fid: {
|
|
102
|
-
value: audits['max-potential-fid']?.numericValue ?? 0,
|
|
103
|
-
score: audits['max-potential-fid']?.score ?? 0,
|
|
104
|
-
displayValue: audits['max-potential-fid']?.displayValue ?? '',
|
|
105
|
-
},
|
|
106
|
-
cls: {
|
|
107
|
-
value: audits['cumulative-layout-shift']?.numericValue ?? 0,
|
|
108
|
-
score: audits['cumulative-layout-shift']?.score ?? 0,
|
|
109
|
-
displayValue: audits['cumulative-layout-shift']?.displayValue ?? '',
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
// Additional metrics
|
|
113
|
-
fcp: audits['first-contentful-paint']?.numericValue ?? 0,
|
|
114
|
-
ttfb: audits['server-response-time']?.numericValue ?? 0,
|
|
115
|
-
tti: audits['interactive']?.numericValue ?? 0,
|
|
116
|
-
tbt: audits['total-blocking-time']?.numericValue ?? 0,
|
|
117
|
-
speedIndex: audits['speed-index']?.numericValue ?? 0,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Extract optimization opportunities
|
|
122
|
-
function extractOpportunities(lhr: LH.Result): Opportunity[] {
|
|
123
|
-
const opportunityAudits = [
|
|
124
|
-
'render-blocking-resources',
|
|
125
|
-
'unused-css-rules',
|
|
126
|
-
'unused-javascript',
|
|
127
|
-
'modern-image-formats',
|
|
128
|
-
'offscreen-images',
|
|
129
|
-
'unminified-css',
|
|
130
|
-
'unminified-javascript',
|
|
131
|
-
'efficient-animated-content',
|
|
132
|
-
'duplicated-javascript',
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
return opportunityAudits
|
|
136
|
-
.map(id => lhr.audits[id])
|
|
137
|
-
.filter(audit => audit && audit.score !== null && audit.score < 1)
|
|
138
|
-
.map(audit => ({
|
|
139
|
-
id: audit.id,
|
|
140
|
-
title: audit.title,
|
|
141
|
-
description: audit.description,
|
|
142
|
-
savings: audit.details?.overallSavingsMs ?? 0,
|
|
143
|
-
items: audit.details?.items ?? [],
|
|
144
|
-
}))
|
|
145
|
-
.sort((a, b) => b.savings - a.savings);
|
|
24
|
+
const result = await lighthouse(url, { port: chrome.port, onlyCategories: ['performance'] });
|
|
25
|
+
await chrome.kill();
|
|
26
|
+
return result?.lhr;
|
|
146
27
|
}
|
|
147
28
|
```
|
|
148
29
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
import { onLCP, onFID, onCLS, onINP, onTTFB } from 'web-vitals';
|
|
153
|
-
|
|
154
|
-
interface WebVitalsReport {
|
|
155
|
-
lcp: VitalMetric;
|
|
156
|
-
fid: VitalMetric;
|
|
157
|
-
cls: VitalMetric;
|
|
158
|
-
inp: VitalMetric;
|
|
159
|
-
ttfb: VitalMetric;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
interface VitalMetric {
|
|
163
|
-
value: number;
|
|
164
|
-
rating: 'good' | 'needs-improvement' | 'poor';
|
|
165
|
-
entries: PerformanceEntry[];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Real User Monitoring (RUM) setup
|
|
169
|
-
function initWebVitalsMonitoring(onReport: (report: WebVitalsReport) => void): void {
|
|
170
|
-
const metrics: Partial<WebVitalsReport> = {};
|
|
171
|
-
|
|
172
|
-
const reportIfComplete = () => {
|
|
173
|
-
if (metrics.lcp && metrics.fid && metrics.cls && metrics.inp && metrics.ttfb) {
|
|
174
|
-
onReport(metrics as WebVitalsReport);
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
onLCP((metric) => {
|
|
179
|
-
metrics.lcp = {
|
|
180
|
-
value: metric.value,
|
|
181
|
-
rating: metric.rating,
|
|
182
|
-
entries: metric.entries,
|
|
183
|
-
};
|
|
184
|
-
reportIfComplete();
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
onFID((metric) => {
|
|
188
|
-
metrics.fid = {
|
|
189
|
-
value: metric.value,
|
|
190
|
-
rating: metric.rating,
|
|
191
|
-
entries: metric.entries,
|
|
192
|
-
};
|
|
193
|
-
reportIfComplete();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
onCLS((metric) => {
|
|
197
|
-
metrics.cls = {
|
|
198
|
-
value: metric.value,
|
|
199
|
-
rating: metric.rating,
|
|
200
|
-
entries: metric.entries,
|
|
201
|
-
};
|
|
202
|
-
reportIfComplete();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
onINP((metric) => {
|
|
206
|
-
metrics.inp = {
|
|
207
|
-
value: metric.value,
|
|
208
|
-
rating: metric.rating,
|
|
209
|
-
entries: metric.entries,
|
|
210
|
-
};
|
|
211
|
-
reportIfComplete();
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
onTTFB((metric) => {
|
|
215
|
-
metrics.ttfb = {
|
|
216
|
-
value: metric.value,
|
|
217
|
-
rating: metric.rating,
|
|
218
|
-
entries: metric.entries,
|
|
219
|
-
};
|
|
220
|
-
reportIfComplete();
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Web Vitals thresholds
|
|
225
|
-
const WEB_VITALS_THRESHOLDS = {
|
|
226
|
-
LCP: { good: 2500, poor: 4000 }, // milliseconds
|
|
227
|
-
FID: { good: 100, poor: 300 }, // milliseconds
|
|
228
|
-
CLS: { good: 0.1, poor: 0.25 }, // score
|
|
229
|
-
INP: { good: 200, poor: 500 }, // milliseconds
|
|
230
|
-
TTFB: { good: 800, poor: 1800 }, // milliseconds
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
// Analyze and provide recommendations
|
|
234
|
-
function analyzeWebVitals(report: WebVitalsReport): VitalsAnalysis {
|
|
235
|
-
const issues: VitalIssue[] = [];
|
|
236
|
-
|
|
237
|
-
// LCP analysis
|
|
238
|
-
if (report.lcp.rating !== 'good') {
|
|
239
|
-
const lcpElement = report.lcp.entries[0]?.element;
|
|
240
|
-
issues.push({
|
|
241
|
-
metric: 'LCP',
|
|
242
|
-
value: report.lcp.value,
|
|
243
|
-
rating: report.lcp.rating,
|
|
244
|
-
element: lcpElement,
|
|
245
|
-
recommendations: getLCPRecommendations(report.lcp, lcpElement),
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// CLS analysis
|
|
250
|
-
if (report.cls.rating !== 'good') {
|
|
251
|
-
const clsSources = report.cls.entries.flatMap(
|
|
252
|
-
entry => (entry as LayoutShift).sources || []
|
|
253
|
-
);
|
|
254
|
-
issues.push({
|
|
255
|
-
metric: 'CLS',
|
|
256
|
-
value: report.cls.value,
|
|
257
|
-
rating: report.cls.rating,
|
|
258
|
-
sources: clsSources,
|
|
259
|
-
recommendations: getCLSRecommendations(clsSources),
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// INP analysis
|
|
264
|
-
if (report.inp.rating !== 'good') {
|
|
265
|
-
issues.push({
|
|
266
|
-
metric: 'INP',
|
|
267
|
-
value: report.inp.value,
|
|
268
|
-
rating: report.inp.rating,
|
|
269
|
-
recommendations: getINPRecommendations(report.inp),
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
overallRating: calculateOverallRating(report),
|
|
275
|
-
issues,
|
|
276
|
-
score: calculateVitalsScore(report),
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// LCP optimization recommendations
|
|
281
|
-
function getLCPRecommendations(metric: VitalMetric, element?: Element): string[] {
|
|
282
|
-
const recommendations: string[] = [];
|
|
283
|
-
|
|
284
|
-
if (element?.tagName === 'IMG') {
|
|
285
|
-
recommendations.push(
|
|
286
|
-
'Add fetchpriority="high" to the LCP image',
|
|
287
|
-
'Preload the LCP image: <link rel="preload" as="image" href="...">',
|
|
288
|
-
'Use modern image formats (WebP, AVIF)',
|
|
289
|
-
'Ensure image is properly sized (no layout shift)',
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (metric.value > 4000) {
|
|
294
|
-
recommendations.push(
|
|
295
|
-
'Reduce server response time (TTFB)',
|
|
296
|
-
'Eliminate render-blocking resources',
|
|
297
|
-
'Consider server-side rendering for critical content',
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
recommendations.push(
|
|
302
|
-
'Minimize critical CSS',
|
|
303
|
-
'Use efficient cache policy for static assets',
|
|
304
|
-
'Consider using a CDN',
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
return recommendations;
|
|
308
|
-
}
|
|
30
|
+
```bash
|
|
31
|
+
# CLI audit
|
|
32
|
+
npx lighthouse https://example.com --output=json --output-path=./report.json
|
|
309
33
|
```
|
|
310
34
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
import CDP from 'chrome-remote-interface';
|
|
315
|
-
|
|
316
|
-
interface PerformanceTrace {
|
|
317
|
-
timelineEvents: TimelineEvent[];
|
|
318
|
-
networkRequests: NetworkRequest[];
|
|
319
|
-
jsProfile: CPUProfile;
|
|
320
|
-
heapSnapshot: HeapSnapshot;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Connect to Chrome DevTools
|
|
324
|
-
async function connectToDevTools(port: number = 9222): Promise<CDP.Client> {
|
|
325
|
-
const client = await CDP({ port });
|
|
326
|
-
|
|
327
|
-
// Enable required domains
|
|
328
|
-
await Promise.all([
|
|
329
|
-
client.Page.enable(),
|
|
330
|
-
client.Network.enable(),
|
|
331
|
-
client.Performance.enable(),
|
|
332
|
-
client.Profiler.enable(),
|
|
333
|
-
client.HeapProfiler.enable(),
|
|
334
|
-
]);
|
|
335
|
-
|
|
336
|
-
return client;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Capture performance trace
|
|
340
|
-
async function capturePerformanceTrace(
|
|
341
|
-
client: CDP.Client,
|
|
342
|
-
url: string
|
|
343
|
-
): Promise<PerformanceTrace> {
|
|
344
|
-
const networkRequests: NetworkRequest[] = [];
|
|
345
|
-
const timelineEvents: TimelineEvent[] = [];
|
|
346
|
-
|
|
347
|
-
// Collect network requests
|
|
348
|
-
client.Network.requestWillBeSent(params => {
|
|
349
|
-
networkRequests.push({
|
|
350
|
-
requestId: params.requestId,
|
|
351
|
-
url: params.request.url,
|
|
352
|
-
method: params.request.method,
|
|
353
|
-
timestamp: params.timestamp,
|
|
354
|
-
type: params.type,
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
client.Network.responseReceived(params => {
|
|
359
|
-
const request = networkRequests.find(r => r.requestId === params.requestId);
|
|
360
|
-
if (request) {
|
|
361
|
-
request.status = params.response.status;
|
|
362
|
-
request.mimeType = params.response.mimeType;
|
|
363
|
-
request.encodedDataLength = params.response.encodedDataLength;
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// Start tracing
|
|
368
|
-
await client.Tracing.start({
|
|
369
|
-
categories: [
|
|
370
|
-
'devtools.timeline',
|
|
371
|
-
'v8.execute',
|
|
372
|
-
'blink.user_timing',
|
|
373
|
-
'loading',
|
|
374
|
-
'painting',
|
|
375
|
-
].join(','),
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// Navigate to page
|
|
379
|
-
await client.Page.navigate({ url });
|
|
380
|
-
await client.Page.loadEventFired();
|
|
381
|
-
|
|
382
|
-
// Wait for network idle
|
|
383
|
-
await waitForNetworkIdle(client);
|
|
384
|
-
|
|
385
|
-
// Stop tracing
|
|
386
|
-
const { value: traceData } = await client.Tracing.tracingComplete();
|
|
387
|
-
|
|
388
|
-
// Capture JS profile
|
|
389
|
-
await client.Profiler.start();
|
|
390
|
-
await new Promise(r => setTimeout(r, 2000)); // Profile for 2 seconds
|
|
391
|
-
const { profile: jsProfile } = await client.Profiler.stop();
|
|
392
|
-
|
|
393
|
-
// Capture heap snapshot
|
|
394
|
-
const heapChunks: string[] = [];
|
|
395
|
-
client.HeapProfiler.addHeapSnapshotChunk(({ chunk }) => heapChunks.push(chunk));
|
|
396
|
-
await client.HeapProfiler.takeHeapSnapshot();
|
|
397
|
-
const heapSnapshot = JSON.parse(heapChunks.join(''));
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
timelineEvents: parseTraceData(traceData),
|
|
401
|
-
networkRequests,
|
|
402
|
-
jsProfile,
|
|
403
|
-
heapSnapshot,
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Analyze network waterfall
|
|
408
|
-
function analyzeNetworkWaterfall(requests: NetworkRequest[]): WaterfallAnalysis {
|
|
409
|
-
const sorted = [...requests].sort((a, b) => a.timestamp - b.timestamp);
|
|
410
|
-
const totalTime = sorted[sorted.length - 1]?.endTime - sorted[0]?.timestamp;
|
|
411
|
-
|
|
412
|
-
// Identify critical path
|
|
413
|
-
const criticalPath = sorted.filter(r =>
|
|
414
|
-
r.type === 'Document' ||
|
|
415
|
-
(r.type === 'Script' && !r.url.includes('async')) ||
|
|
416
|
-
r.type === 'Stylesheet'
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// Find blocking resources
|
|
420
|
-
const blockingResources = sorted.filter(r =>
|
|
421
|
-
r.renderBlocking ||
|
|
422
|
-
(r.type === 'Script' && !r.async && !r.defer)
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
// Calculate metrics
|
|
426
|
-
return {
|
|
427
|
-
totalRequests: requests.length,
|
|
428
|
-
totalTime,
|
|
429
|
-
totalSize: requests.reduce((sum, r) => sum + (r.encodedDataLength || 0), 0),
|
|
430
|
-
criticalPath,
|
|
431
|
-
blockingResources,
|
|
432
|
-
byType: groupByType(requests),
|
|
433
|
-
recommendations: generateWaterfallRecommendations(requests),
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Memory leak detection
|
|
438
|
-
async function detectMemoryLeaks(
|
|
439
|
-
client: CDP.Client,
|
|
440
|
-
actions: () => Promise<void>,
|
|
441
|
-
iterations: number = 3
|
|
442
|
-
): Promise<MemoryLeakReport> {
|
|
443
|
-
const snapshots: HeapSnapshot[] = [];
|
|
444
|
-
|
|
445
|
-
// Take initial snapshot
|
|
446
|
-
snapshots.push(await takeHeapSnapshot(client));
|
|
35
|
+
## Features
|
|
447
36
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
37
|
+
| Feature | Description | Guide |
|
|
38
|
+
|---------|-------------|-------|
|
|
39
|
+
| Lighthouse Audits | Automated performance scoring | Run in CI/CD, track scores over time |
|
|
40
|
+
| Core Web Vitals | LCP, FID, CLS, INP metrics | Monitor with web-vitals library |
|
|
41
|
+
| Bundle Analysis | JavaScript bundle size inspection | Use webpack-bundle-analyzer or rollup-plugin-visualizer |
|
|
42
|
+
| Network Waterfall | Request timing and blocking analysis | Identify render-blocking resources |
|
|
43
|
+
| Memory Profiling | Heap snapshots and leak detection | Compare snapshots before/after operations |
|
|
44
|
+
| Performance Budgets | Automated regression prevention | Set thresholds in CI pipeline |
|
|
455
45
|
|
|
456
|
-
|
|
457
|
-
const heapGrowth = analyzeHeapGrowth(snapshots);
|
|
458
|
-
const retainedObjects = findRetainedObjects(snapshots);
|
|
46
|
+
## Common Patterns
|
|
459
47
|
|
|
460
|
-
|
|
461
|
-
hasLeak: heapGrowth.trend === 'increasing',
|
|
462
|
-
heapGrowth,
|
|
463
|
-
retainedObjects,
|
|
464
|
-
recommendations: generateMemoryRecommendations(retainedObjects),
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### 4. Bundle Analysis
|
|
48
|
+
### Core Web Vitals Monitoring
|
|
470
49
|
|
|
471
50
|
```typescript
|
|
472
|
-
import {
|
|
473
|
-
import { visualizer } from 'rollup-plugin-visualizer';
|
|
474
|
-
|
|
475
|
-
interface BundleAnalysis {
|
|
476
|
-
totalSize: number;
|
|
477
|
-
gzipSize: number;
|
|
478
|
-
modules: ModuleInfo[];
|
|
479
|
-
duplicates: DuplicateModule[];
|
|
480
|
-
largestModules: ModuleInfo[];
|
|
481
|
-
recommendations: string[];
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Webpack bundle analysis
|
|
485
|
-
function createBundleAnalyzerConfig(): BundleAnalyzerPlugin {
|
|
486
|
-
return new BundleAnalyzerPlugin({
|
|
487
|
-
analyzerMode: 'json',
|
|
488
|
-
reportFilename: 'bundle-report.json',
|
|
489
|
-
generateStatsFile: true,
|
|
490
|
-
statsFilename: 'bundle-stats.json',
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Analyze bundle stats
|
|
495
|
-
async function analyzeBundleStats(statsPath: string): Promise<BundleAnalysis> {
|
|
496
|
-
const stats = JSON.parse(await fs.readFile(statsPath, 'utf-8'));
|
|
497
|
-
|
|
498
|
-
const modules = flattenModules(stats.modules);
|
|
499
|
-
const duplicates = findDuplicates(modules);
|
|
51
|
+
import { onLCP, onFID, onCLS, onINP } from 'web-vitals';
|
|
500
52
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const gzipSize = await calculateGzipSize(statsPath);
|
|
504
|
-
|
|
505
|
-
// Sort by size
|
|
506
|
-
const largestModules = [...modules]
|
|
507
|
-
.sort((a, b) => b.size - a.size)
|
|
508
|
-
.slice(0, 20);
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
totalSize,
|
|
512
|
-
gzipSize,
|
|
513
|
-
modules,
|
|
514
|
-
duplicates,
|
|
515
|
-
largestModules,
|
|
516
|
-
recommendations: generateBundleRecommendations({
|
|
517
|
-
totalSize,
|
|
518
|
-
duplicates,
|
|
519
|
-
largestModules,
|
|
520
|
-
}),
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Find duplicate modules
|
|
525
|
-
function findDuplicates(modules: ModuleInfo[]): DuplicateModule[] {
|
|
526
|
-
const modulesByName = new Map<string, ModuleInfo[]>();
|
|
527
|
-
|
|
528
|
-
for (const module of modules) {
|
|
529
|
-
const name = getModuleName(module.identifier);
|
|
530
|
-
const existing = modulesByName.get(name) || [];
|
|
531
|
-
existing.push(module);
|
|
532
|
-
modulesByName.set(name, existing);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return Array.from(modulesByName.entries())
|
|
536
|
-
.filter(([_, instances]) => instances.length > 1)
|
|
537
|
-
.map(([name, instances]) => ({
|
|
538
|
-
name,
|
|
539
|
-
instances: instances.length,
|
|
540
|
-
totalSize: instances.reduce((sum, m) => sum + m.size, 0),
|
|
541
|
-
versions: [...new Set(instances.map(m => getModuleVersion(m.identifier)))],
|
|
542
|
-
}))
|
|
543
|
-
.sort((a, b) => b.totalSize - a.totalSize);
|
|
53
|
+
function sendToAnalytics(metric: { name: string; value: number; rating: string }) {
|
|
54
|
+
analytics.track('web_vital', metric);
|
|
544
55
|
}
|
|
545
56
|
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
largestModules: ModuleInfo[];
|
|
551
|
-
}): string[] {
|
|
552
|
-
const recommendations: string[] = [];
|
|
553
|
-
|
|
554
|
-
// Size recommendations
|
|
555
|
-
if (analysis.totalSize > 500 * 1024) {
|
|
556
|
-
recommendations.push(
|
|
557
|
-
'Consider code splitting to reduce initial bundle size',
|
|
558
|
-
'Use dynamic imports for routes and large components',
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Duplicate recommendations
|
|
563
|
-
if (analysis.duplicates.length > 0) {
|
|
564
|
-
recommendations.push(
|
|
565
|
-
`Found ${analysis.duplicates.length} duplicate packages - dedupe with npm/yarn`,
|
|
566
|
-
'Consider using webpack.optimize.ModuleConcatenationPlugin',
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
const biggestDupe = analysis.duplicates[0];
|
|
570
|
-
if (biggestDupe) {
|
|
571
|
-
recommendations.push(
|
|
572
|
-
`Largest duplicate: ${biggestDupe.name} (${formatBytes(biggestDupe.totalSize)})`,
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// Large module recommendations
|
|
578
|
-
const veryLargeModules = analysis.largestModules.filter(m => m.size > 100 * 1024);
|
|
579
|
-
if (veryLargeModules.length > 0) {
|
|
580
|
-
recommendations.push(
|
|
581
|
-
'Large modules detected - consider lazy loading:',
|
|
582
|
-
...veryLargeModules.slice(0, 3).map(m =>
|
|
583
|
-
` - ${m.name}: ${formatBytes(m.size)}`
|
|
584
|
-
),
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return recommendations;
|
|
589
|
-
}
|
|
57
|
+
onLCP(sendToAnalytics); // Target: < 2.5s
|
|
58
|
+
onFID(sendToAnalytics); // Target: < 100ms
|
|
59
|
+
onCLS(sendToAnalytics); // Target: < 0.1
|
|
60
|
+
onINP(sendToAnalytics); // Target: < 200ms
|
|
590
61
|
```
|
|
591
62
|
|
|
592
|
-
###
|
|
63
|
+
### Performance Budget Check
|
|
593
64
|
|
|
594
65
|
```typescript
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
interface MetricBudget {
|
|
601
|
-
metric: string;
|
|
602
|
-
budget: number;
|
|
603
|
-
unit: 'ms' | 'score' | 'bytes';
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
interface ResourceBudget {
|
|
607
|
-
type: string;
|
|
608
|
-
budget: number;
|
|
609
|
-
count?: number;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Default performance budget
|
|
613
|
-
const DEFAULT_BUDGET: PerformanceBudget = {
|
|
614
|
-
metrics: [
|
|
615
|
-
{ metric: 'LCP', budget: 2500, unit: 'ms' },
|
|
616
|
-
{ metric: 'FID', budget: 100, unit: 'ms' },
|
|
617
|
-
{ metric: 'CLS', budget: 0.1, unit: 'score' },
|
|
618
|
-
{ metric: 'TTI', budget: 3800, unit: 'ms' },
|
|
619
|
-
{ metric: 'TBT', budget: 200, unit: 'ms' },
|
|
620
|
-
],
|
|
621
|
-
resources: [
|
|
622
|
-
{ type: 'script', budget: 300 * 1024 },
|
|
623
|
-
{ type: 'stylesheet', budget: 50 * 1024 },
|
|
624
|
-
{ type: 'image', budget: 500 * 1024 },
|
|
625
|
-
{ type: 'font', budget: 100 * 1024 },
|
|
626
|
-
{ type: 'total', budget: 1000 * 1024 },
|
|
627
|
-
],
|
|
66
|
+
const BUDGET = {
|
|
67
|
+
lcp: 2500, fid: 100, cls: 0.1, tti: 3800, tbt: 200,
|
|
68
|
+
jsSize: 300 * 1024, cssSize: 50 * 1024, totalSize: 1000 * 1024,
|
|
628
69
|
};
|
|
629
70
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
): BudgetReport {
|
|
636
|
-
const violations: BudgetViolation[] = [];
|
|
637
|
-
|
|
638
|
-
// Check metric budgets
|
|
639
|
-
for (const { metric, budget: limit, unit } of budget.metrics) {
|
|
640
|
-
const actual = metrics[metric.toLowerCase()];
|
|
641
|
-
if (actual > limit) {
|
|
642
|
-
violations.push({
|
|
643
|
-
type: 'metric',
|
|
644
|
-
name: metric,
|
|
645
|
-
budget: limit,
|
|
646
|
-
actual,
|
|
647
|
-
unit,
|
|
648
|
-
overBy: ((actual - limit) / limit * 100).toFixed(1) + '%',
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Check resource budgets
|
|
654
|
-
for (const { type, budget: limit, count } of budget.resources) {
|
|
655
|
-
const resource = resources[type];
|
|
656
|
-
if (resource.size > limit) {
|
|
657
|
-
violations.push({
|
|
658
|
-
type: 'resource',
|
|
659
|
-
name: type,
|
|
660
|
-
budget: limit,
|
|
661
|
-
actual: resource.size,
|
|
662
|
-
unit: 'bytes',
|
|
663
|
-
overBy: formatBytes(resource.size - limit),
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
if (count && resource.count > count) {
|
|
667
|
-
violations.push({
|
|
668
|
-
type: 'resource-count',
|
|
669
|
-
name: `${type} count`,
|
|
670
|
-
budget: count,
|
|
671
|
-
actual: resource.count,
|
|
672
|
-
unit: 'requests',
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return {
|
|
678
|
-
passed: violations.length === 0,
|
|
679
|
-
violations,
|
|
680
|
-
summary: generateBudgetSummary(violations),
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Performance regression test
|
|
685
|
-
async function runPerformanceRegression(
|
|
686
|
-
url: string,
|
|
687
|
-
baseline: PerformanceMetrics,
|
|
688
|
-
threshold: number = 0.1 // 10% regression threshold
|
|
689
|
-
): Promise<RegressionReport> {
|
|
690
|
-
const current = await runLighthouseAudit({ url });
|
|
691
|
-
const regressions: Regression[] = [];
|
|
692
|
-
|
|
693
|
-
for (const [metric, baselineValue] of Object.entries(baseline)) {
|
|
694
|
-
const currentValue = current.metrics[metric];
|
|
695
|
-
const change = (currentValue - baselineValue) / baselineValue;
|
|
696
|
-
|
|
697
|
-
if (change > threshold) {
|
|
698
|
-
regressions.push({
|
|
699
|
-
metric,
|
|
700
|
-
baseline: baselineValue,
|
|
701
|
-
current: currentValue,
|
|
702
|
-
change: `+${(change * 100).toFixed(1)}%`,
|
|
703
|
-
severity: change > 0.25 ? 'critical' : 'warning',
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
return {
|
|
709
|
-
passed: regressions.length === 0,
|
|
710
|
-
regressions,
|
|
711
|
-
metrics: current.metrics,
|
|
712
|
-
comparison: generateComparison(baseline, current.metrics),
|
|
713
|
-
};
|
|
71
|
+
function checkBudget(metrics: Record<string, number>) {
|
|
72
|
+
const violations = Object.entries(BUDGET)
|
|
73
|
+
.filter(([key, limit]) => metrics[key] > limit)
|
|
74
|
+
.map(([key, limit]) => ({ metric: key, limit, actual: metrics[key] }));
|
|
75
|
+
return { passed: violations.length === 0, violations };
|
|
714
76
|
}
|
|
715
77
|
```
|
|
716
78
|
|
|
717
|
-
###
|
|
79
|
+
### CI/CD Integration
|
|
718
80
|
|
|
719
81
|
```yaml
|
|
720
82
|
# .github/workflows/performance.yml
|
|
721
|
-
name:
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
branches: [main]
|
|
728
|
-
|
|
729
|
-
jobs:
|
|
730
|
-
lighthouse:
|
|
731
|
-
runs-on: ubuntu-latest
|
|
732
|
-
steps:
|
|
733
|
-
- uses: actions/checkout@v4
|
|
734
|
-
|
|
735
|
-
- name: Setup Node.js
|
|
736
|
-
uses: actions/setup-node@v4
|
|
737
|
-
with:
|
|
738
|
-
node-version: '20'
|
|
739
|
-
|
|
740
|
-
- name: Install dependencies
|
|
741
|
-
run: npm ci
|
|
742
|
-
|
|
743
|
-
- name: Build
|
|
744
|
-
run: npm run build
|
|
745
|
-
|
|
746
|
-
- name: Start server
|
|
747
|
-
run: npm run start &
|
|
748
|
-
|
|
749
|
-
- name: Run Lighthouse
|
|
750
|
-
uses: treosh/lighthouse-ci-action@v10
|
|
751
|
-
with:
|
|
752
|
-
urls: |
|
|
753
|
-
http://localhost:3000
|
|
754
|
-
http://localhost:3000/products
|
|
755
|
-
budgetPath: ./performance-budget.json
|
|
756
|
-
uploadArtifacts: true
|
|
757
|
-
|
|
758
|
-
- name: Check performance budget
|
|
759
|
-
run: |
|
|
760
|
-
node scripts/check-performance-budget.js \
|
|
761
|
-
--budget ./performance-budget.json \
|
|
762
|
-
--results .lighthouseci/*.json
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
## Use Cases
|
|
766
|
-
|
|
767
|
-
### 1. E-commerce Performance Optimization
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
// E-commerce specific performance audit
|
|
771
|
-
async function auditEcommerceSite(baseUrl: string): Promise<EcommerceAudit> {
|
|
772
|
-
const pages = [
|
|
773
|
-
{ name: 'Home', path: '/' },
|
|
774
|
-
{ name: 'Category', path: '/products' },
|
|
775
|
-
{ name: 'Product', path: '/products/1' },
|
|
776
|
-
{ name: 'Cart', path: '/cart' },
|
|
777
|
-
{ name: 'Checkout', path: '/checkout' },
|
|
778
|
-
];
|
|
779
|
-
|
|
780
|
-
const results = await Promise.all(
|
|
781
|
-
pages.map(async page => ({
|
|
782
|
-
...page,
|
|
783
|
-
audit: await runLighthouseAudit({
|
|
784
|
-
url: `${baseUrl}${page.path}`,
|
|
785
|
-
device: 'mobile',
|
|
786
|
-
}),
|
|
787
|
-
}))
|
|
788
|
-
);
|
|
789
|
-
|
|
790
|
-
return {
|
|
791
|
-
pages: results,
|
|
792
|
-
criticalPath: identifyCriticalPath(results),
|
|
793
|
-
conversionImpact: estimateConversionImpact(results),
|
|
794
|
-
recommendations: prioritizeRecommendations(results),
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### 2. SPA Performance Monitoring
|
|
800
|
-
|
|
801
|
-
```typescript
|
|
802
|
-
// Single Page App performance monitoring
|
|
803
|
-
function initSPAPerformanceMonitoring(): void {
|
|
804
|
-
// Track route changes
|
|
805
|
-
let routeStartTime: number;
|
|
806
|
-
|
|
807
|
-
router.beforeEach(() => {
|
|
808
|
-
routeStartTime = performance.now();
|
|
809
|
-
});
|
|
810
|
-
|
|
811
|
-
router.afterEach((to) => {
|
|
812
|
-
const routeTime = performance.now() - routeStartTime;
|
|
813
|
-
|
|
814
|
-
// Track custom metric
|
|
815
|
-
performance.measure(`route-${to.name}`, {
|
|
816
|
-
start: routeStartTime,
|
|
817
|
-
duration: routeTime,
|
|
818
|
-
});
|
|
819
|
-
|
|
820
|
-
// Report to analytics
|
|
821
|
-
analytics.track('route_performance', {
|
|
822
|
-
route: to.name,
|
|
823
|
-
duration: routeTime,
|
|
824
|
-
timestamp: Date.now(),
|
|
825
|
-
});
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
// Monitor long tasks
|
|
829
|
-
const observer = new PerformanceObserver((list) => {
|
|
830
|
-
for (const entry of list.getEntries()) {
|
|
831
|
-
if (entry.duration > 50) {
|
|
832
|
-
console.warn('Long task detected:', entry.duration, 'ms');
|
|
833
|
-
analytics.track('long_task', {
|
|
834
|
-
duration: entry.duration,
|
|
835
|
-
startTime: entry.startTime,
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
|
|
841
|
-
observer.observe({ entryTypes: ['longtask'] });
|
|
842
|
-
}
|
|
83
|
+
- name: Lighthouse CI
|
|
84
|
+
uses: treosh/lighthouse-ci-action@v10
|
|
85
|
+
with:
|
|
86
|
+
urls: http://localhost:3000
|
|
87
|
+
budgetPath: ./performance-budget.json
|
|
88
|
+
uploadArtifacts: true
|
|
843
89
|
```
|
|
844
90
|
|
|
845
91
|
## Best Practices
|
|
846
92
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
### Don'ts
|
|
857
|
-
|
|
858
|
-
- Don't test only on fast connections
|
|
859
|
-
- Don't ignore mobile performance
|
|
860
|
-
- Don't optimize prematurely without data
|
|
861
|
-
- Don't rely solely on synthetic testing
|
|
862
|
-
- Don't forget about third-party scripts
|
|
863
|
-
- Don't ignore cumulative layout shift
|
|
864
|
-
|
|
865
|
-
### Performance Checklist
|
|
866
|
-
|
|
867
|
-
```markdown
|
|
868
|
-
## Pre-Launch Performance Checklist
|
|
869
|
-
|
|
870
|
-
### Core Web Vitals
|
|
871
|
-
- [ ] LCP < 2.5s on mobile
|
|
872
|
-
- [ ] FID < 100ms
|
|
873
|
-
- [ ] CLS < 0.1
|
|
874
|
-
- [ ] INP < 200ms
|
|
875
|
-
|
|
876
|
-
### Resource Optimization
|
|
877
|
-
- [ ] Images optimized (WebP/AVIF, lazy loaded)
|
|
878
|
-
- [ ] JavaScript bundled and minified
|
|
879
|
-
- [ ] CSS optimized and critical CSS inlined
|
|
880
|
-
- [ ] Fonts optimized (preload, font-display)
|
|
881
|
-
|
|
882
|
-
### Caching
|
|
883
|
-
- [ ] Proper cache headers set
|
|
884
|
-
- [ ] Service worker for offline support
|
|
885
|
-
- [ ] CDN configured
|
|
886
|
-
|
|
887
|
-
### Monitoring
|
|
888
|
-
- [ ] RUM implemented
|
|
889
|
-
- [ ] Performance budget defined
|
|
890
|
-
- [ ] CI/CD performance tests
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
## Related Skills
|
|
894
|
-
|
|
895
|
-
- **frontend-design** - UI performance considerations
|
|
896
|
-
- **devops** - Infrastructure optimization
|
|
897
|
-
- **docker** - Container performance
|
|
898
|
-
- **kubernetes** - Scaling for performance
|
|
899
|
-
|
|
900
|
-
## Reference Resources
|
|
901
|
-
|
|
902
|
-
- [Web Vitals](https://web.dev/vitals/)
|
|
903
|
-
- [Lighthouse Documentation](https://developer.chrome.com/docs/lighthouse/)
|
|
904
|
-
- [Chrome DevTools Performance](https://developer.chrome.com/docs/devtools/performance/)
|
|
905
|
-
- [web.dev Performance](https://web.dev/learn/performance/)
|
|
93
|
+
| Do | Avoid |
|
|
94
|
+
|----|-------|
|
|
95
|
+
| Test with throttled network and CPU | Testing only on fast connections |
|
|
96
|
+
| Monitor real user metrics (RUM) | Relying solely on synthetic tests |
|
|
97
|
+
| Set and enforce performance budgets | Optimizing without baseline data |
|
|
98
|
+
| Preload LCP images with fetchpriority | Ignoring mobile performance |
|
|
99
|
+
| Lazy load below-fold content | Loading all JS upfront |
|
|
100
|
+
| Use modern image formats (WebP, AVIF) | Ignoring third-party script impact |
|
|
101
|
+
| Profile before and after changes | Skipping cumulative layout shift fixes |
|