Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:53:15 +08:00
commit 16f4bfc62c
21 changed files with 6077 additions and 0 deletions

160
skills/codebase-patterns.md Normal file
View File

@@ -0,0 +1,160 @@
---
name: codebase-patterns
description: Common codebase patterns, anti-patterns, and architectural conventions. Auto-loaded when analyzing code structure or discussing architecture.
---
# Common Codebase Patterns
## Architecture Patterns
### MVC (Model-View-Controller)
- **Models**: Data and business logic
- **Views**: UI/presentation layer
- **Controllers**: Handle requests, coordinate models/views
- Common in: Rails, Django, Laravel, ASP.NET MVC
### Layered Architecture
- **Presentation**: UI components
- **Business Logic**: Core application logic
- **Data Access**: Database interactions
- **Common separation**: `/controllers`, `/services`, `/repositories`
### Microservices
- Independent services with own databases
- Communication via REST/gRPC/message queues
- Look for: Docker, Kubernetes configs, service directories
### Repository Pattern
```
/repositories
- UserRepository.ts
- ProductRepository.ts
```
Abstracts data access logic
### Service Layer Pattern
```
/services
- AuthService.ts
- PaymentService.ts
```
Business logic separation from controllers
## Common Anti-Patterns
### God Class/Object
- Class with too many responsibilities
- **Signs**: 500+ lines, handles everything
- **Fix**: Split into smaller, focused classes
### Spaghetti Code
- Complex, tangled dependencies
- **Signs**: Hard to follow flow, circular dependencies
- **Fix**: Refactor to clear dependencies
### Magic Numbers/Strings
```javascript
// Bad
if (status === 1) { }
// Good
const STATUS_ACTIVE = 1;
if (status === STATUS_ACTIVE) { }
```
### N+1 Query Problem
```python
# Bad - N+1 queries
for user in users:
user.posts # Separate query for each user
# Good - Single query
users = User.objects.prefetch_related('posts')
```
## File Organization Patterns
### Feature-based
```
/features
/auth
- login.ts
- signup.ts
/dashboard
- dashboard.ts
```
### Layer-based
```
/controllers
/models
/views
/services
```
### Domain-driven Design
```
/domain
/user
/order
/payment
```
## Naming Conventions
### Functions
- **JavaScript/TypeScript**: `camelCase` - `getUserById()`
- **Python**: `snake_case` - `get_user_by_id()`
- **Go**: `PascalCase` for exported - `GetUserByID()`
### Classes
- **All languages**: `PascalCase` - `UserController`, `PaymentService`
### Constants
- **All languages**: `SCREAMING_SNAKE_CASE` - `MAX_RETRIES`, `API_URL`
### Files
- **React**: `PascalCase.tsx` - `UserProfile.tsx`
- **Others**: `kebab-case` or `snake_case` - `user-service.ts`
## Common Directory Structures
### React/Next.js
```
/app or /pages
/components
/lib or /utils
/hooks
/styles
/public
```
### Express/Node
```
/routes
/controllers
/models
/middleware
/services
/config
```
### Django
```
/app_name
/models
/views
/templates
/migrations
```
### Go
```
/cmd
/internal
/pkg
/api
/configs
```
Use these patterns to quickly understand and navigate unfamiliar codebases.

View File

@@ -0,0 +1,305 @@
---
name: performance-patterns
description: Performance optimization patterns and common bottlenecks across languages. Auto-loaded when discussing performance or optimization.
---
# Performance Optimization Patterns
## Common Performance Bottlenecks
### 1. N+1 Query Problem
**Problem**: Making a separate database query for each item in a loop
**Bad:**
```python
# Django ORM
users = User.objects.all()
for user in users:
print(user.profile.bio) # Separate query for each user!
```
**Good:**
```python
users = User.objects.select_related('profile').all()
for user in users:
print(user.profile.bio) # Single query with JOIN
```
### 2. Unnecessary Re-renders (React)
**Bad:**
```jsx
function Component({ items }) {
// This creates new object every render!
const config = { limit: 10, offset: 0 };
return <List items={items} config={config} />;
}
```
**Good:**
```jsx
const DEFAULT_CONFIG = { limit: 10, offset: 0 };
function Component({ items }) {
return <List items={items} config={DEFAULT_CONFIG} />;
}
// Or use useMemo for computed values
const config = useMemo(() => ({
limit: 10,
offset: page * 10
}), [page]);
```
### 3. Synchronous Operations in Loops
**Bad:**
```javascript
// Sequential - takes 5 seconds for 5 items
for (const item of items) {
await fetchData(item); // 1 second each
}
```
**Good:**
```javascript
// Parallel - takes ~1 second total
await Promise.all(items.map(item => fetchData(item)));
// Or with concurrency limit
const pLimit = require('p-limit');
const limit = pLimit(5);
await Promise.all(items.map(item => limit(() => fetchData(item))));
```
### 4. Large Bundle Sizes
**Bad:**
```javascript
import _ from 'lodash'; // Imports entire library (71KB)
import moment from 'moment'; // 289KB!
```
**Good:**
```javascript
import debounce from 'lodash/debounce'; // Only what you need
import { format } from 'date-fns'; // 12KB vs 289KB
```
### 5. Inefficient Algorithms
**Bad:** O(n²)
```python
def has_duplicates(arr):
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] == arr[j]:
return True
return False
```
**Good:** O(n)
```python
def has_duplicates(arr):
return len(arr) != len(set(arr))
```
## Optimization Techniques
### Caching
#### Memoization
```javascript
const cache = new Map();
function expensiveCalculation(n) {
if (cache.has(n)) {
return cache.get(n);
}
const result = /* expensive work */;
cache.set(n, result);
return result;
}
```
#### React Query / SWR
```javascript
// Automatic caching, deduplication, and background refetching
const { data } = useQuery('users', fetchUsers);
```
#### HTTP Caching
```javascript
// Set cache headers
res.set('Cache-Control', 'public, max-age=3600');
```
### Lazy Loading
#### Dynamic Imports
```javascript
// Instead of importing at top
// import HeavyComponent from './HeavyComponent';
// Load only when needed
const HeavyComponent = lazy(() => import('./HeavyComponent'));
<Suspense fallback={<Spinner />}>
<HeavyComponent />
</Suspense>
```
#### Image Lazy Loading
```html
<img src="image.jpg" loading="lazy" />
```
### Debouncing & Throttling
```javascript
// Debounce: Wait until user stops typing
const debouncedSearch = debounce((query) => {
searchAPI(query);
}, 300);
// Throttle: Execute at most once per interval
const throttledScroll = throttle(() => {
handleScroll();
}, 100);
```
### Database Optimization
#### Index Critical Columns
```sql
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
```
#### Use Pagination
```javascript
// Bad: Load all records
const users = await User.find();
// Good: Paginate
const users = await User.find()
.limit(20)
.skip(page * 20);
```
#### Eager Loading
```python
# Django: Avoid N+1
posts = Post.objects.select_related('author').prefetch_related('comments')
# SQLAlchemy
posts = session.query(Post).options(
joinedload(Post.author),
subqueryload(Post.comments)
).all()
```
### Frontend Optimization
#### Virtual Scrolling
```jsx
// For long lists, only render visible items
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={600}
itemCount={10000}
itemSize={35}
>
{Row}
</FixedSizeList>
```
#### Code Splitting
```javascript
// Webpack
const routes = [
{
path: '/dashboard',
component: () => import('./Dashboard'),
},
{
path: '/profile',
component: () => import('./Profile'),
},
];
```
#### Web Workers
```javascript
// Offload heavy computation
const worker = new Worker('worker.js');
worker.postMessage(hugeDataset);
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
```
## Language-Specific Optimizations
### JavaScript/TypeScript
- Use `Set` for lookups instead of `Array.includes()`
- Use `Map` instead of objects for frequent additions/deletions
- Avoid `delete` operator, set to `undefined` instead
- Use `for` loops instead of `.forEach()` for hot paths
### Python
- Use list comprehensions instead of loops
- Use generators for large datasets
- Use `__slots__` for classes with many instances
- Use `set` for membership testing
### Go
- Reuse buffers instead of allocating new ones
- Use sync.Pool for frequently allocated objects
- Prefer value types over pointers for small structs
- Use buffered channels appropriately
### Java
- Use `StringBuilder` for string concatenation in loops
- Prefer `ArrayList` over `LinkedList` (usually)
- Use primitive types instead of wrappers when possible
- Set appropriate initial capacity for collections
## Performance Monitoring
### Profiling Tools
- **JavaScript**: Chrome DevTools, Lighthouse
- **Python**: cProfile, py-spy
- **Go**: pprof
- **Java**: JProfiler, VisualVM
### Key Metrics
- **Time to First Byte (TTFB)**: Server response time
- **First Contentful Paint (FCP)**: When content appears
- **Largest Contentful Paint (LCP)**: Main content loaded (<2.5s)
- **Time to Interactive (TTI)**: When page is interactive
- **Total Blocking Time (TBT)**: How long main thread is blocked
### Quick Checks
```bash
# Measure API response time
time curl https://api.example.com/endpoint
# Check bundle size
npx webpack-bundle-analyzer dist/stats.json
# Profile Python script
python -m cProfile -s cumtime script.py
# Memory usage
/usr/bin/time -v ./program
```
## Golden Rules
1. **Profile before optimizing** - Measure, don't guess
2. **Optimize the critical path** - Focus on what users experience
3. **Start with algorithms** - O(n²) → O(n log n) matters more than micro-optimizations
4. **Cache intelligently** - But watch for stale data
5. **Lazy load everything possible** - Don't load what you don't need
6. **Monitor in production** - Real user performance matters most
Remember: Premature optimization is the root of all evil. Focus on bottlenecks, not micro-optimizations.

256
skills/refactoring-guide.md Normal file
View File

@@ -0,0 +1,256 @@
---
name: refactoring-guide
description: Practical refactoring techniques and when to apply them. Auto-loaded when discussing code improvements or refactoring.
---
# Refactoring Guide
## When to Refactor
### Red Flags
- Functions longer than 50 lines
- Classes with 10+ methods
- Nested conditionals 3+ levels deep
- Duplicated code in 3+ places
- Complex boolean conditions
- High cyclomatic complexity (>10)
## Common Refactorings
### Extract Function
**Before:**
```javascript
function processOrder(order) {
// Calculate total
let total = 0;
for (let item of order.items) {
total += item.price * item.quantity;
}
// Apply discount
if (order.customer.isPremium) {
total *= 0.9;
}
return total;
}
```
**After:**
```javascript
function processOrder(order) {
const subtotal = calculateSubtotal(order.items);
return applyDiscount(subtotal, order.customer);
}
function calculateSubtotal(items) {
return items.reduce((sum, item) =>
sum + item.price * item.quantity, 0);
}
function applyDiscount(amount, customer) {
return customer.isPremium ? amount * 0.9 : amount;
}
```
### Replace Conditional with Polymorphism
**Before:**
```python
def calculate_pay(employee):
if employee.type == "manager":
return employee.salary + employee.bonus
elif employee.type == "engineer":
return employee.salary + employee.stock_options
else:
return employee.salary
```
**After:**
```python
class Employee:
def calculate_pay(self):
return self.salary
class Manager(Employee):
def calculate_pay(self):
return self.salary + self.bonus
class Engineer(Employee):
def calculate_pay(self):
return self.salary + self.stock_options
```
### Extract Constant
**Before:**
```javascript
if (age >= 18 && age <= 65) {
// eligible
}
```
**After:**
```javascript
const MIN_ELIGIBLE_AGE = 18;
const MAX_ELIGIBLE_AGE = 65;
if (age >= MIN_ELIGIBLE_AGE && age <= MAX_ELIGIBLE_AGE) {
// eligible
}
```
### Introduce Parameter Object
**Before:**
```typescript
function createUser(
name: string,
email: string,
age: number,
address: string,
phone: string
) {
// ...
}
```
**After:**
```typescript
interface UserData {
name: string;
email: string;
age: number;
address: string;
phone: string;
}
function createUser(userData: UserData) {
// ...
}
```
### Replace Magic Number with Named Constant
**Before:**
```go
func calculateDiscount(price float64) float64 {
return price * 0.15
}
```
**After:**
```go
const LOYALTY_DISCOUNT_RATE = 0.15
func calculateDiscount(price float64) float64 {
return price * LOYALTY_DISCOUNT_RATE
}
```
### Decompose Conditional
**Before:**
```javascript
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
} else {
charge = quantity * summerRate;
}
```
**After:**
```javascript
if (isWinter(date)) {
charge = winterCharge(quantity);
} else {
charge = summerCharge(quantity);
}
function isWinter(date) {
return date.before(SUMMER_START) || date.after(SUMMER_END);
}
function winterCharge(quantity) {
return quantity * winterRate + winterServiceCharge;
}
function summerCharge(quantity) {
return quantity * summerRate;
}
```
### Replace Nested Conditional with Guard Clauses
**Before:**
```python
def get_payment(employee):
if employee.is_active:
if employee.has_salary:
return employee.salary
else:
return 0
else:
return 0
```
**After:**
```python
def get_payment(employee):
if not employee.is_active:
return 0
if not employee.has_salary:
return 0
return employee.salary
```
## Large-Scale Refactorings
### Split Module
When a file is too large (500+ lines), split by responsibility:
```
user.ts (1000 lines)
user-model.ts (200 lines)
user-service.ts (300 lines)
user-validator.ts (150 lines)
user-utils.ts (100 lines)
```
### Extract Service Layer
Move business logic from controllers:
```
Before: Controller has 500 lines with business logic
After:
- Controller: 100 lines (request handling)
- Service: 400 lines (business logic)
```
### Introduce Repository
Abstract database access:
```
Before: Direct SQL/ORM calls throughout code
After: Repository classes handle all data access
```
## Refactoring Workflow
1. **Ensure tests exist** (or write them first)
2. **Make small changes** (one refactoring at a time)
3. **Run tests** after each change
4. **Commit frequently** with clear messages
5. **Don't mix refactoring with feature work**
## Red Flags to Watch For
- **Premature optimization**: Don't refactor for performance without profiling
- **Over-engineering**: Don't add complexity for theoretical future needs
- **Breaking changes**: Be careful with public APIs
- **Scope creep**: Stick to planned refactoring, don't expand mid-work
## Quick Wins
### Low-hanging fruit for immediate improvement:
1. Rename unclear variables
2. Extract magic numbers to constants
3. Remove commented-out code
4. Fix inconsistent formatting
5. Add missing error handling
6. Remove unused imports/functions
7. Simplify boolean expressions
These refactorings make code more maintainable and easier to understand.

550
skills/runtime-debugging.md Normal file
View File

@@ -0,0 +1,550 @@
# Runtime Debugging Patterns
Advanced techniques for debugging web application freezes, hangs, and performance issues.
## Common Runtime Issues
### 1. Memory Leaks
**Symptoms:**
- Gradually increasing memory usage
- Browser tab becomes unresponsive over time
- Page crashes with "Aw, Snap!" error
**Common Causes:**
```javascript
// Detached DOM nodes
let elements = [];
document.addEventListener('click', () => {
const div = document.createElement('div');
document.body.appendChild(div);
elements.push(div); // Keeping reference after removal
document.body.removeChild(div); // DOM node removed but still in memory
});
// Event listener accumulation
class Component {
constructor() {
// Missing cleanup in componentWillUnmount/ngOnDestroy
window.addEventListener('resize', this.handleResize);
}
}
// Closure traps
function createLeak() {
const largeData = new Array(1000000).fill('data');
return function() {
// largeData is retained even if not used
console.log('callback');
};
}
```
**Detection:**
- Use Chrome DevTools Memory Profiler
- Take heap snapshots and compare
- Look for objects with increasing retain counts
### 2. Infinite Loops
**JavaScript Infinite Loops:**
```javascript
// Synchronous infinite loop - blocks immediately
while (condition) {
// condition never becomes false
}
// Recursive setTimeout - gradual degradation
function loop() {
// Do something
setTimeout(loop, 0); // Keeps scheduling
}
```
**Angular Change Detection Loops:**
```typescript
// Component causing infinite change detection
@Component({
template: '{{ getComputedValue() }}'
})
class BadComponent {
getComputedValue() {
// Returns different value each time
return Math.random(); // Triggers new change detection
}
}
// Zone.js microtask explosion
someObservable.subscribe(value => {
this.property = value;
// Triggers change detection
this.anotherObservable.next(this.property);
// Which triggers another change...
});
```
**React Re-render Loops:**
```jsx
// Missing dependencies
function Component() {
const [state, setState] = useState(0);
useEffect(() => {
setState(state + 1); // Missing dependency array
}); // Runs on every render
return <div>{state}</div>;
}
// Object/Array recreation
function Parent() {
// New object every render
const config = { key: 'value' };
return <Child config={config} />;
// Child re-renders every time
}
```
### 3. Main Thread Blocking
**Long-Running JavaScript:**
```javascript
// Heavy computation on main thread
function processLargeDataset(data) {
for (let i = 0; i < 1000000; i++) {
// Complex calculations
data[i] = expensiveOperation(data[i]);
}
}
// Solution: Use Web Workers
const worker = new Worker('processor.js');
worker.postMessage({ cmd: 'process', data: largeData });
worker.onmessage = (e) => {
// Handle processed data
};
// Or chunk the work
async function processInChunks(data) {
const chunkSize = 100;
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
processChunk(chunk);
// Yield to browser
await new Promise(resolve => setTimeout(resolve, 0));
}
}
```
### 4. Network Request Issues
**Request Queue Blocking:**
```javascript
// Browser limits concurrent requests (6 per domain)
for (let i = 0; i < 100; i++) {
fetch(`/api/item/${i}`); // Only 6 run in parallel
}
// Solution: Batch requests
const ids = Array.from({ length: 100 }, (_, i) => i);
fetch('/api/items/batch', {
method: 'POST',
body: JSON.stringify({ ids })
});
```
**CORS Preflight Accumulation:**
```javascript
// Each cross-origin request triggers OPTIONS
for (let endpoint of endpoints) {
fetch(endpoint, {
headers: {
'Custom-Header': 'value' // Triggers preflight
}
});
}
```
## Framework-Specific Patterns
### Angular Debugging
**Zone.js Task Tracking:**
```typescript
// Monitor Zone.js tasks
Zone.current.fork({
name: 'debugZone',
onScheduleTask: (delegate, current, target, task) => {
console.log('Task scheduled:', task.type, task.source);
return delegate.scheduleTask(target, task);
},
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
console.log('Task invoked:', task.type);
return delegate.invokeTask(target, task, applyThis, applyArgs);
}
}).run(() => {
// Your Angular app code
});
```
**Change Detection Profiling:**
```typescript
// Enable Angular DevTools Profiler
import { enableDebugTools } from '@angular/platform-browser';
import { ApplicationRef } from '@angular/core';
platformBrowserDynamic().bootstrapModule(AppModule)
.then(module => {
const appRef = module.injector.get(ApplicationRef);
const componentRef = appRef.components[0];
enableDebugTools(componentRef);
// Now use: ng.profiler.timeChangeDetection()
});
```
### React Debugging
**React Profiler API:**
```jsx
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration) {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
}
<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>
```
**Why Did You Render:**
```javascript
// Setup why-did-you-render
import React from 'react';
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
});
}
// Track specific component
Component.whyDidYouRender = true;
```
### Vue Debugging
**Vue Performance Tracking:**
```javascript
// Vue 2
Vue.config.performance = true;
// Vue 3
const app = createApp(App);
app.config.performance = true;
// Now use Performance DevTools to see component timings
```
## Browser APIs for Debugging
### Performance Observer API
```javascript
// Monitor long tasks
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long task detected:', {
duration: entry.duration,
startTime: entry.startTime,
name: entry.name
});
// Send to analytics
analytics.track('long_task', {
duration: entry.duration,
url: window.location.href
});
}
}
});
observer.observe({
entryTypes: ['longtask', 'measure', 'navigation']
});
```
### Memory API
```javascript
// Monitor memory usage (Chrome only)
if (performance.memory) {
setInterval(() => {
const memInfo = performance.memory;
const usedMB = Math.round(memInfo.usedJSHeapSize / 1048576);
const totalMB = Math.round(memInfo.totalJSHeapSize / 1048576);
const limitMB = Math.round(memInfo.jsHeapSizeLimit / 1048576);
console.log(`Memory: ${usedMB}/${totalMB}MB (limit: ${limitMB}MB)`);
// Alert if using >90% of limit
if (memInfo.usedJSHeapSize > memInfo.jsHeapSizeLimit * 0.9) {
console.error('Memory usage critical!');
}
}, 5000);
}
```
### Request Idle Callback
```javascript
// Defer non-critical work
const tasksQueue = [];
function scheduleTasks(tasks) {
tasksQueue.push(...tasks);
processTaskQueue();
}
function processTaskQueue(deadline) {
while (deadline.timeRemaining() > 0 && tasksQueue.length > 0) {
const task = tasksQueue.shift();
task();
}
if (tasksQueue.length > 0) {
requestIdleCallback(processTaskQueue);
}
}
requestIdleCallback(processTaskQueue);
```
## Headless Browser Debugging
### Puppeteer Performance Monitoring
```javascript
const puppeteer = require('puppeteer');
async function analyzePerformance(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Enable CDP domains
const client = await page.target().createCDPSession();
await client.send('Performance.enable');
await client.send('Network.enable');
// Collect metrics
const metrics = [];
// Monitor metrics over time
const interval = setInterval(async () => {
const { metrics: perfMetrics } = await client.send('Performance.getMetrics');
const timestamp = Date.now();
metrics.push({
timestamp,
heap: perfMetrics.find(m => m.name === 'JSHeapUsedSize').value,
documents: perfMetrics.find(m => m.name === 'Documents').value,
frames: perfMetrics.find(m => m.name === 'Frames').value,
nodes: perfMetrics.find(m => m.name === 'Nodes').value,
layoutCount: perfMetrics.find(m => m.name === 'LayoutCount').value,
jsEventListeners: perfMetrics.find(m => m.name === 'JSEventListeners').value
});
}, 1000);
// Navigate and wait
await page.goto(url, { waitUntil: 'networkidle0' });
await page.waitForTimeout(10000);
clearInterval(interval);
// Analyze trends
const analysis = analyzeMetricTrends(metrics);
await browser.close();
return analysis;
}
function analyzeMetricTrends(metrics) {
const heapGrowth = metrics[metrics.length - 1].heap - metrics[0].heap;
const avgHeapGrowthPerSec = heapGrowth / metrics.length;
return {
memoryLeak: avgHeapGrowthPerSec > 100000, // >100KB/s growth
domLeak: metrics[metrics.length - 1].nodes > 1500,
listenerLeak: metrics[metrics.length - 1].jsEventListeners > 500,
metrics: metrics
};
}
```
### Playwright Network Monitoring
```javascript
const { chromium } = require('playwright');
async function monitorNetworkPerformance(url) {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
const slowRequests = [];
const failedRequests = [];
// Monitor all requests
page.on('request', request => {
request._startTime = Date.now();
});
page.on('response', response => {
const request = response.request();
const duration = Date.now() - request._startTime;
if (duration > 3000) {
slowRequests.push({
url: request.url(),
method: request.method(),
duration: duration,
status: response.status()
});
}
});
page.on('requestfailed', request => {
failedRequests.push({
url: request.url(),
method: request.method(),
failure: request.failure()
});
});
await page.goto(url);
await page.waitForTimeout(10000);
await browser.close();
return { slowRequests, failedRequests };
}
```
## Production Monitoring
### Client-Side Monitoring Script
```javascript
// Add to your production app
(function() {
const perfData = {
longTasks: [],
errors: [],
metrics: []
};
// Long task monitoring
if (window.PerformanceObserver) {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
perfData.longTasks.push({
duration: entry.duration,
timestamp: Date.now()
});
// Send to backend if critical
if (entry.duration > 1000) {
sendToAnalytics('critical_long_task', {
duration: entry.duration,
url: window.location.href
});
}
}
}).observe({ entryTypes: ['longtask'] });
}
// Error monitoring
window.addEventListener('error', (e) => {
perfData.errors.push({
message: e.message,
stack: e.error?.stack,
timestamp: Date.now()
});
sendToAnalytics('js_error', {
message: e.message,
url: window.location.href
});
});
// Periodic metrics collection
setInterval(() => {
if (performance.memory) {
perfData.metrics.push({
heap: performance.memory.usedJSHeapSize,
timestamp: Date.now()
});
}
}, 10000);
// Send aggregated data on page unload
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('/api/performance', JSON.stringify(perfData));
});
function sendToAnalytics(event, data) {
// Your analytics implementation
if (typeof gtag !== 'undefined') {
gtag('event', event, data);
}
}
})();
```
## Quick Diagnosis Checklist
When app freezes/hangs:
1. **Check Console**: Any errors or warnings?
2. **Check Network Tab**: Pending requests?
3. **Check Performance Tab**: Recording shows long tasks?
4. **Check Memory Tab**: Heap size growing?
5. **Framework DevTools**:
- Angular: Check Zone.js tasks
- React: Check component renders
- Vue: Check watcher count
6. **Take Heap Snapshot**: Compare before/after
7. **Record CPU Profile**: Identify hot functions
8. **Check Event Listeners**: Growing count?
9. **Inspect DOM**: Node count excessive?
10. **Test in Incognito**: Extensions causing issues?
## Best Practices
1. **Always clean up**:
- Remove event listeners
- Cancel timers/intervals
- Unsubscribe from observables
- Clear WeakMaps/WeakSets
2. **Optimize renders**:
- Use React.memo/useMemo
- Use Angular OnPush strategy
- Use Vue computed properties
3. **Defer work**:
- Use requestIdleCallback
- Use Web Workers
- Implement virtual scrolling
4. **Monitor production**:
- Add performance monitoring
- Track key metrics
- Set up alerts
5. **Test performance**:
- Use Lighthouse CI
- Add performance budgets
- Test on slow devices

View File

@@ -0,0 +1,600 @@
---
name: web-performance-optimization
description: Comprehensive guide to web performance optimization techniques, Core Web Vitals, and best practices across frameworks
---
# Web Performance Optimization Guide
This skill provides comprehensive knowledge about web performance optimization, Core Web Vitals, and practical techniques for improving website speed and user experience.
## Core Web Vitals
### 1. Largest Contentful Paint (LCP)
**What it measures:** Loading performance - time until the largest content element is visible.
**Good:** < 2.5s | **Needs Improvement:** 2.5s - 4.0s | **Poor:** > 4.0s
**Common causes of poor LCP:**
- Slow server response times
- Render-blocking JavaScript and CSS
- Slow resource load times
- Client-side rendering
**Optimization strategies:**
```html
<!-- Preload critical resources -->
<link rel="preload" as="image" href="hero-image.jpg">
<link rel="preload" as="font" href="font.woff2" type="font/woff2" crossorigin>
<!-- Optimize images -->
<img src="hero.webp" alt="Hero" loading="eager" fetchpriority="high">
<!-- Lazy load below-the-fold images -->
<img src="image.jpg" loading="lazy" alt="Description">
```
```javascript
// Implement adaptive loading based on network
if (navigator.connection && navigator.connection.effectiveType === '4g') {
// Load high-res images
} else {
// Load lower-res images
}
```
### 2. First Input Delay (FID) / Interaction to Next Paint (INP)
**What it measures:** Interactivity - time from first user interaction to browser response.
**Good (FID):** < 100ms | **Needs Improvement:** 100ms - 300ms | **Poor:** > 300ms
**Good (INP):** < 200ms | **Needs Improvement:** 200ms - 500ms | **Poor:** > 500ms
**Common causes:**
- Heavy JavaScript execution
- Long tasks blocking main thread
- Large bundle sizes
**Optimization strategies:**
```javascript
// Code splitting - load only what's needed
import('./heavy-module.js').then(module => {
module.init();
});
// Use web workers for heavy computation
const worker = new Worker('worker.js');
worker.postMessage({data: largeDataset});
// Debounce expensive operations
const debouncedSearch = debounce((query) => {
performSearch(query);
}, 300);
// Use requestIdleCallback for non-critical work
requestIdleCallback(() => {
analytics.track('page_view');
});
```
### 3. Cumulative Layout Shift (CLS)
**What it measures:** Visual stability - unexpected layout shifts during page load.
**Good:** < 0.1 | **Needs Improvement:** 0.1 - 0.25 | **Poor:** > 0.25
**Common causes:**
- Images without dimensions
- Dynamically injected content
- Web fonts causing FOIT/FOUT
- Ads, embeds, iframes
**Optimization strategies:**
```html
<!-- Always specify image dimensions -->
<img src="image.jpg" width="800" height="600" alt="Description">
<!-- Use aspect-ratio CSS for responsive images -->
<style>
.responsive-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
</style>
<!-- Reserve space for dynamic content -->
<div style="min-height: 400px;">
<!-- Content loaded here -->
</div>
<!-- Font loading strategies -->
<link rel="preload" as="font" href="font.woff2" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* or optional */
}
</style>
```
## Performance Optimization Techniques
### 1. Resource Optimization
#### Image Optimization
```bash
# Convert to WebP (90% smaller than JPEG)
cwebp input.jpg -q 80 -o output.webp
# Generate responsive images
convert input.jpg -resize 400x output-400w.jpg
convert input.jpg -resize 800x output-800w.jpg
convert input.jpg -resize 1200x output-1200w.jpg
```
```html
<!-- Responsive images with WebP -->
<picture>
<source type="image/webp" srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w">
<source type="image/jpeg" srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w">
<img src="image-800.jpg" alt="Description" loading="lazy">
</picture>
```
#### JavaScript Optimization
```javascript
// Tree shaking - import only what you need
import { debounce } from 'lodash-es'; // Good
// import _ from 'lodash'; // Bad - imports everything
// Dynamic imports for route-based code splitting
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
// Minimize third-party scripts
// Use Partytown for offloading to web worker
<script type="text/partytown">
// Analytics, ads, etc. run in worker
</script>
```
#### CSS Optimization
```html
<!-- Inline critical CSS -->
<style>
/* Above-the-fold styles only */
.header { display: flex; }
.hero { min-height: 400px; }
</style>
<!-- Defer non-critical CSS -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
<!-- Remove unused CSS -->
<!-- Use PurgeCSS, UnCSS, or built-in framework tools -->
```
### 2. Network Optimization
#### Compression
```nginx
# Enable gzip/brotli compression (nginx)
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
# Brotli (better compression)
brotli on;
brotli_types text/plain text/css application/json application/javascript;
```
#### HTTP/2 & HTTP/3
```nginx
# Enable HTTP/2
listen 443 ssl http2;
# Enable HTTP/3 (QUIC)
listen 443 quic reuseport;
add_header Alt-Svc 'h3=":443"; ma=86400';
```
#### Caching Strategy
```javascript
// Service Worker caching
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
'/',
'/styles.css',
'/script.js',
'/logo.svg'
]);
})
);
});
// Cache-first strategy for static assets
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
```
```nginx
# HTTP caching headers
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
```
### 3. Rendering Optimization
#### Critical Rendering Path
```html
<!-- Optimize the critical rendering path -->
<!DOCTYPE html>
<html>
<head>
<!-- Preconnect to required origins -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- Inline critical CSS -->
<style>/* Critical styles */</style>
<!-- Async non-critical CSS -->
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
</head>
<body>
<!-- Content -->
<!-- Defer JavaScript -->
<script defer src="app.js"></script>
</body>
</html>
```
#### Server-Side Rendering (SSR) vs Client-Side Rendering (CSR)
```javascript
// Next.js - Hybrid approach
export async function getServerSideProps() {
// Fetch data on server
const data = await fetch('https://api.example.com/data');
return { props: { data } };
}
// Static generation for faster TTFB
export async function getStaticProps() {
const data = await fetch('https://api.example.com/data');
return {
props: { data },
revalidate: 60 // ISR - revalidate every 60s
};
}
```
### 4. Framework-Specific Optimizations
#### React
```javascript
// Lazy load components
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// Memoization
const MemoizedComponent = React.memo(({ data }) => {
return <div>{data}</div>;
});
// useMemo for expensive computations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// useCallback for function references
const handleClick = useCallback(() => {
doSomething(a);
}, [a]);
// Virtualization for long lists
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={400}
itemCount={1000}
itemSize={50}
>
{Row}
</FixedSizeList>
```
#### Vue
```javascript
// Async components
const AsyncComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
);
// Keep-alive for component caching
<keep-alive>
<component :is="currentView" />
</keep-alive>
// v-once for static content
<div v-once>{{ staticContent }}</div>
// Virtual scrolling
import { RecycleScroller } from 'vue-virtual-scroller';
<RecycleScroller
:items="items"
:item-size="50"
>
</RecycleScroller>
```
#### Angular
```typescript
// Lazy loading routes
const routes: Routes = [
{
path: 'feature',
loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}
];
// OnPush change detection
@Component({
selector: 'app-component',
changeDetection: ChangeDetectionStrategy.OnPush
})
// TrackBy for *ngFor
<div *ngFor="let item of items; trackBy: trackByFn">
{{ item.name }}
</div>
trackByFn(index, item) {
return item.id;
}
```
## Database & Backend Optimization
### N+1 Query Problem
```javascript
// Bad - N+1 queries
const users = await User.findAll();
for (const user of users) {
const posts = await Post.findAll({ where: { userId: user.id } });
}
// Good - Single query with join
const users = await User.findAll({
include: [{ model: Post }]
});
```
### Caching Strategies
```javascript
// Redis caching
const cached = await redis.get(`user:${id}`);
if (cached) {
return JSON.parse(cached);
}
const user = await db.users.findById(id);
await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 3600);
return user;
// Memoization
const memoize = (fn) => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
};
```
### Database Indexing
```sql
-- Create indexes for frequently queried columns
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_user_id ON posts(user_id);
-- Composite indexes for multi-column queries
CREATE INDEX idx_posts_user_date ON posts(user_id, created_at);
-- Analyze query performance
EXPLAIN ANALYZE SELECT * FROM posts WHERE user_id = 123;
```
## Performance Monitoring
### Real User Monitoring (RUM)
```javascript
// Web Vitals API
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
// Send to analytics
function sendToAnalytics({name, value, id}) {
analytics.track('web-vital', {
metric: name,
value: Math.round(value),
id
});
}
getCLS(sendToAnalytics);
getLCP(sendToAnalytics);
```
### Performance Observer API
```javascript
// Observe long tasks
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long task detected:', entry);
}
}
});
observer.observe({entryTypes: ['longtask']});
// Monitor resource timing
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Resource:', entry.name, 'Duration:', entry.duration);
}
}).observe({entryTypes: ['resource']});
```
## Common Performance Anti-Patterns
### 1. Blocking the Main Thread
```javascript
// Bad - blocks UI
for (let i = 0; i < 1000000; i++) {
// Heavy computation
}
// Good - chunk work
function processInChunks(data, chunkSize = 100) {
let index = 0;
function processChunk() {
const chunk = data.slice(index, index + chunkSize);
// Process chunk
index += chunkSize;
if (index < data.length) {
requestIdleCallback(processChunk);
}
}
processChunk();
}
```
### 2. Memory Leaks
```javascript
// Bad - creates memory leak
class Component {
constructor() {
setInterval(() => {
this.updateState();
}, 1000);
}
}
// Good - cleanup
class Component {
constructor() {
this.interval = setInterval(() => {
this.updateState();
}, 1000);
}
destroy() {
clearInterval(this.interval);
}
}
```
### 3. Excessive Re-renders
```javascript
// Bad - re-renders on every parent update
function Child({ items }) {
return items.map(item => <div key={item.id}>{item.name}</div>);
}
// Good - memoized
const Child = React.memo(function Child({ items }) {
return items.map(item => <div key={item.id}>{item.name}</div>);
}, (prevProps, nextProps) => {
return prevProps.items === nextProps.items;
});
```
## Performance Budget
Set and enforce performance budgets:
```json
{
"budgets": [
{
"resourceSizes": [
{"resourceType": "script", "budget": 300},
{"resourceType": "image", "budget": 500},
{"resourceType": "stylesheet", "budget": 50}
],
"resourceCounts": [
{"resourceType": "third-party", "budget": 10}
],
"timings": [
{"metric": "interactive", "budget": 3000},
{"metric": "first-contentful-paint", "budget": 1500}
]
}
]
}
```
## Testing Tools
- **Lighthouse**: Automated audits
- **WebPageTest**: Real device testing
- **Chrome DevTools**: Performance profiling
- **bundlephobia.com**: Check package sizes
- **web.dev**: Best practices and guides
## Key Takeaways
1. **Measure first**: Use Lighthouse, Web Vitals, RUM
2. **Optimize critical path**: Inline critical CSS, defer JS
3. **Reduce bundle size**: Code splitting, tree shaking
4. **Optimize images**: WebP, lazy loading, responsive images
5. **Cache effectively**: Service workers, HTTP caching
6. **Minimize main thread work**: Web workers, chunking
7. **Monitor continuously**: Real user monitoring
8. **Set budgets**: Enforce performance standards