Initial commit
This commit is contained in:
160
skills/codebase-patterns.md
Normal file
160
skills/codebase-patterns.md
Normal 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.
|
||||
305
skills/performance-patterns.md
Normal file
305
skills/performance-patterns.md
Normal 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
256
skills/refactoring-guide.md
Normal 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
550
skills/runtime-debugging.md
Normal 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
|
||||
600
skills/web-performance-optimization.md
Normal file
600
skills/web-performance-optimization.md
Normal 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
|
||||
Reference in New Issue
Block a user