К основному содержанию
скилл CX Codex

js-performance-patterns — скилл для Claude Code: как установить

Набор паттернов микрооптимизации JavaScript для горячих путей. Помогает агенту устранять просадки скорости в циклах, обработчиках событий и операциях с DOM.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add PatternsDev/skills --skill js-performance-patterns -a claude-code
Открыть на GitHub

Что делает js-performance-patterns

Скилл содержит правила оптимизации рантайма для JavaScript и TypeScript. Инструкции охватывают частые узкие места: поиск по коллекциям через Set и Map вместо линейного перебора массивов, объединение цепочек .filter().map() в один цикл и кэширование длины массива при итерациях по 10 000+ элементам. Для браузерного кода описано разделение чтения и записи в DOM. Они предотвращают layout thrashing.

Паттерны не заменяют смену алгоритма. Поэтому переписывать цепочки методов имеет смысл только после устранения квадратичной сложности. Автор прямо запрещает внедрять эти микрооптимизации в холодные участки, где читаемость важнее экономии наносекунд. Правила применимы к React и Node.js при обработке массивов от 1000 записей либо частых событий анимации.

Какие задачи решает

  • ускорить поиск по коллекциям заменой массивов на Set и Map
  • предотвратить layout thrashing разделением чтения и записи в DOM
  • объединить несколько проходов .filter() и .map() в один цикл
  • закэшировать результаты тяжёлых чистых функций через мемоизацию

Примеры запросов агенту

  • Оптимизируй горячий цикл в src/parser.ts с помощью js-performance-patterns
  • Проверь этот обработчик скролла на layout thrashing и сгруппируй вызовы к DOM
  • Перепиши цепочку filter и map в один проход по массиву

Как установить js-performance-patterns

1Через Skills CLI (Vercel)

npx skills add PatternsDev/skills --skill js-performance-patterns -a claude-code

2Вручную: положить файлы

~/.claude/skills/js-performance-patterns/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/js-performance-patterns/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о js-performance-patterns

Как установить js-performance-patterns в Claude Code?

Выполните команду npx skills add PatternsDev/skills --skill js-performance-patterns -a claude-code. Навык сохранится в ~/.claude/skills/js-performance-patterns/SKILL.md.

Для какого кода подходит js-performance-patterns?

Скилл создан для JavaScript и TypeScript в Node.js и браузерных окружениях. Правила применяются к горячим путям: обработке массивов от 1000 элементов, анимациям и частым событиям вроде scroll или resize.

Заменяют ли эти паттерны оптимизацию алгоритмов?

Нет, инструкция требует сначала исправить сложность алгоритма, например сократить O(n²) до O(n). Микрооптимизации применяют только после профилирования к участкам с доказанной нагрузкой.

Исходный текст

SKILL.md · MIT
---
name: js-performance-patterns
description: Provides framework-agnostic JavaScript runtime performance patterns. Use when optimizing hot paths, loops, DOM operations, caching, or data structure choices in performance-critical code.
paths:
  - "**/*.js"
  - "**/*.ts"
license: MIT
metadata:
  author: patterns.dev
  version: "1.1"
related_skills:
  - "module-pattern"
  - "singleton-pattern"
---

# JavaScript Performance Patterns

## Table of Contents

- [When to Use](#when-to-use)
- [Instructions](#instructions)
- [Details](#details)
- [Source](#source)

Runtime performance micro-patterns for JavaScript hot paths. These patterns matter most in tight loops, frequent callbacks (scroll, resize, animation frames), and data-heavy operations. They apply to any JavaScript environment — React, Vue, vanilla, Node.js.

## When to Use

Reference these patterns when:
- Profiling reveals a hot function or tight loop
- Processing large datasets (1,000+ items)
- Handling high-frequency events (scroll, mousemove, resize)
- Optimizing build-time or server-side scripts
- Reviewing code for performance in critical paths

## Instructions

- Apply these patterns only in **measured hot paths** — code that runs frequently or processes large datasets. Don't apply them to cold code paths where readability is more important than nanosecond gains.

## Details

### Overview

Micro-optimizations are **not** a substitute for algorithmic improvements. Address the algorithm first (O(n^2) to O(n), removing waterfalls, reducing DOM mutations). Once the algorithm is right, these patterns squeeze additional performance from hot paths.

---

### 1. Use `Set` and `Map` for Lookups

**Impact: HIGH for large collections** — O(1) vs O(n) per lookup.

Array methods like `.includes()`, `.find()`, and `.indexOf()` scan linearly. For repeated lookups against the same collection, convert to `Set` or `Map` first.

**Avoid — O(n) per check:**

```typescript
const allowedIds = ['a', 'b', 'c', /* ...hundreds more */]

function isAllowed(id: string) {
  return allowedIds.includes(id) // scans entire array
}

items.filter(item => allowedIds.includes(item.id)) // O(n * m)
```

**Prefer — O(1) per check:**

```typescript
const allowedIds = new Set(['a', 'b', 'c', /* ...hundreds more */])

function isAllowed(id: string) {
  return allowedIds.has(id)
}

items.filter(item => allowedIds.has(item.id)) // O(n)
```

For key-value lookups, use `Map` instead of scanning an array of objects:

```typescript
// Avoid
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
const user = users.find(u => u.id === targetId) // O(n)

// Prefer
const userMap = new Map(users.map(u => [u.id, u]))
const user = userMap.get(targetId) // O(1)
```

---

### 2. Batch DOM Reads and Writes

**Impact: HIGH** — Prevents layout thrashing.

Interleaving DOM reads (e.g., `offsetHeight`, `getBoundingClientRect`) with DOM writes (e.g., `style.height = ...`) forces the browser to recalculate layout multiple times. Batch all reads first, then all writes.

**Avoid — layout thrashing (read/write/read/write):**

```typescript
elements.forEach(el => {
  const height = el.offsetHeight    // read → forces layout
  el.style.height = `${height * 2}px` // write
})
// Each iteration forces a layout recalculation
```

**Prefer — batched reads then writes:**

```typescript
// Read phase
const heights = elements.map(el => el.offsetHeight)

// Write phase
elements.forEach((el, i) => {
  el.style.height = `${heights[i] * 2}px`
})
```

For complex cases, use `requestAnimationFrame` to defer writes to the next frame, or use a library like [fastdom](https://github.com/wilsonpage/fastdom).

**CSS class approach — single reflow:**

```typescript
// Avoid multiple style mutations
el.style.width = '100px'
el.style.height = '200px'
el.style.margin = '10px'

// Prefer — one reflow
el.classList.add('expanded')
// or
el.style.cssText = 'width:100px;height:200px;margin:10px;'
```

---

### 3. Cache Property Access in Tight Loops

**Impact: MEDIUM** — Reduces repeated property resolution.

Accessing deeply nested properties or array `.length` in every iteration adds overhead in tight loops.

**Avoid:**

```typescript
for (let i = 0; i < data.items.length; i++) {
  process(data.items[i].value.nested.prop)
}
```

**Prefer:**

```typescript
const { items } = data
for (let i = 0, len = items.length; i < len; i++) {
  const val = items[i].value.nested.prop
  process(val)
}
```

This matters for arrays with 10,000+ items or when called at 60fps. For small arrays or infrequent calls, the readable version is fine.

---

### 4. Memoize Expensive Function Results

**Impact: MEDIUM-HIGH** — Avoids recomputing the same result.

When a pure function is called repeatedly with the same arguments, cache the result.

**Simple single-value cache:**

```typescript
function memoize<T extends (...args: any[]) => any>(fn: T): T {
  let lastArgs: any[] | undefined
  let lastResult: any

  return ((...args: any[]) => {
    if (lastArgs && args.every((arg, i) => Object.is(arg, lastArgs![i]))) {
      return lastResult
    }
    lastArgs = args
    lastResult = fn(...args)
    return lastResult
  }) as T
}

const expensiveCalc = memoize((data: number[]) => {
  return data.reduce((sum, n) => sum + heavyTransform(n), 0)
})
```

**Multi-key cache with Map:**

```typescript
const cache = new Map<string, Result>()

function getResult(key: string): Result {
  if (cache.has(key)) return cache.get(key)!
  const result = computeExpensiveResult(key)
  cache.set(key, result)
  return result
}
```

For caches that can grow unbounded, use an LRU strategy or `WeakMap` for object keys.

---

### 5. Combine Iterations Over the Same Data

**Impact: MEDIUM** — Single pass instead of multiple.

Chaining `.filter().map().reduce()` creates intermediate arrays and iterates the data multiple times. For large arrays in hot paths, combine into a single loop.

**Avoid — 3 iterations, 2 intermediate arrays:**

```typescript
const result = users
  .filter(u => u.active)
  .map(u => u.name)
  .reduce((acc, name) => acc + name + ', ', '')
```

**Prefer — single pass:**

```typescript
let result = ''
for (const u of users) {
  if (u.active) {
    result += u.name + ', '
  }
}
```

For small arrays (< 100 items), the chained version is fine and more readable. Optimize only when profiling shows it matters.

---

### 6. Short-Circuit with Length Checks First

**Impact: LOW-MEDIUM** — Avoids expensive operations on empty inputs.

Before running expensive comparisons or transformations, check if the input is empty.

```typescript
function findMatchingItems(items: Item[], query: string): Item[] {
  if (items.length === 0 || query.length === 0) return []

  const normalized = query.toLowerCase()
  return items.filter(item =>
    item.name.toLowerCase().includes(normalized)
  )
}
```

---

### 7. Return Early to Skip Unnecessary Work

**Impact: LOW-MEDIUM** — Reduces average-case execution.

Structure functions to exit as soon as possible for common non-matching cases.

**Avoid — always does full work:**

```typescript
function processEvent(event: AppEvent) {
  let result = null
  if (event.type === 'click') {
    if (event.target && event.target.matches('.actionable')) {
      result = handleAction(event)
    }
  }
  return result
}
```

**Prefer — exits early:**

```typescript
function processEvent(event: AppEvent) {
  if (event.type !== 'click') return null
  if (!event.target?.matches('.actionable')) return null
  return handleAction(event)
}
```

---

### 8. Hoist RegExp and Constant Creation Outside Loops

**Impact: LOW-MEDIUM** — Avoids repeated compilation.

Creating RegExp objects or constant values inside loops or frequently-called functions wastes CPU.

**Avoid — compiles regex 10,000 times:**

```typescript
function validate(items: string[]) {
  return items.filter(item => {
    const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
    return pattern.test(item)
  })
}
```

**Prefer — compile once:**

```typescript
const EMAIL_PATTERN = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

function validate(items: string[]) {
  return items.filter(item => EMAIL_PATTERN.test(item))
}
```

---

### 9. Use `toSorted()`, `toReversed()`, `toSpliced()` for Immutability

**Impact: LOW** — Correct immutability without manual copying.

The new non-mutating array methods avoid the `[...arr].sort()` pattern and communicate intent more clearly.

**Avoid — manual copy then mutate:**

```typescript
const sorted = [...items].sort((a, b) => a.price - b.price)
const reversed = [...items].reverse()
const without = [...items]; without.splice(index, 1)
```

**Prefer — non-mutating methods:**

```typescript
const sorted = items.toSorted((a, b) => a.price - b.price)
const reversed = items.toReversed()
const without = items.toSpliced(index, 1)
```

These are available in all modern browsers and Node.js 20+.

---

### 10. Use `requestAnimationFrame` for Visual Updates

**Impact: MEDIUM** — Syncs with the browser's render cycle.

DOM updates triggered outside the rendering cycle (from timers, event handlers, etc.) can cause jank. Batch visual updates inside `requestAnimationFrame`.

**Avoid — updates outside render cycle:**

```typescript
window.addEventListener('scroll', () => {
  progressBar.style.width = `${getScrollPercent()}%`
  counter.textContent = `${getScrollPercent()}%`
}, { passive: true })
```

**Prefer — synced to render:**

```typescript
let ticking = false

window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      const pct = getScrollPercent()
      progressBar.style.width = `${pct}%`
      counter.textContent = `${pct}%`
      ticking = false
    })
    ticking = true
  }
}, { passive: true })
```

---

### 11. Use `structuredClone` for Deep Copies

**Impact: LOW** — Correct deep cloning without libraries.

`structuredClone()` handles circular references, typed arrays, Dates, RegExps, Maps, and Sets — unlike `JSON.parse(JSON.stringify())`.

```typescript
// Avoid — loses Dates, Maps, Sets, undefined values
const copy = JSON.parse(JSON.stringify(original))

// Prefer — handles all standard types
const copy = structuredClone(original)
```

Note: `structuredClone` cannot clone functions or DOM nodes. For those cases, implement a custom clone.

---

### 12. Prefer `Map` Over Plain Objects for Dynamic Keys

**Impact: LOW-MEDIUM** — Better performance for frequent additions/deletions.

V8 optimizes plain objects for static shapes. When keys are added and removed dynamically (caches, counters, registries), `Map` provides consistently better performance.

```typescript
// Avoid for dynamic keys
const counts: Record<string, number> = {}
items.forEach(item => {
  counts[item.category] = (counts[item.category] || 0) + 1
})

// Prefer for dynamic keys
const counts = new Map<string, number>()
items.forEach(item => {
  counts.set(item.category, (counts.get(item.category) ?? 0) + 1)
})
```

---

## Source

Patterns from [patterns.dev](https://www.patterns.dev/) — JavaScript performance guidance for the broader web engineering community.

© patterns.dev · MIT · PatternsDev/skills

Похожие скиллы

Всё по теме «Фронтенд и UI» →
m10-performance actionbook Инструкции для агента по оптимизации Rust-кода: от замеров через бенчмарки до устранения лишних аллокаций памяти. CX Codex +1 3 600установок go-performance cxuu Скилл для оптимизации Go: подсказывает, где заменить fmt на strconv, как задать ёмкость слайса и когда хватит strings.Builder. CX Codex +1 1 200установок roblox-performance sentinelcore Скилл для ИИ-агентов, который помогает находить узкие места в Roblox-игре: просадки FPS, лишние draw calls, тяжёлые циклы. CX Codex +1 826установок mapbox-web-performance-patterns mapbox Скилл учит ИИ-агента оптимизировать приложения на Mapbox GL JS: убирать каскадные задержки данных, уменьшать бандл стилей и переводить маркеры на GPU-слои. CX Codex +1 2 500установок performance-optimization Mindrally Набор правил для веб-производительности: серверный рендеринг, CSS, JavaScript, ассеты и кэш. Полезен, когда агент пишет фронтенд и вы хотите задать ему планку. CX Codex +1 848установок gsap-performance greensock Официальный скилл GSAP по производительности: подсказывает агенту, какие свойства анимировать, а какие ломают кадры. Пригодится, когда анимация дёргается. CX Codex +1 69 400установок

Из того же набора44

server-side-rendering Скилл объясняет серверный рендеринг в React и подсказывает, когда он оправдан, а когда хватит статики или клиентского рендеринга. CX Codex +1 942установок incremental-static-rendering Скилл учит агента настраивать Incremental Static Regeneration в Next.js. Он помогает обновлять статичные страницы без полной пересборки сайта. CX Codex +1 918установок static-rendering Скилл объясняет статическую генерацию: какие страницы можно собрать в HTML заранее и как это сделать в Next.js. Пригодится, когда контент не меняется от запроса к запросу. CX Codex +1 910установок streaming-ssr Учит агента отдавать HTML чанками через renderToPipeableStream и Suspense. Пригодится, когда страница большая и ждать полной отрисовки перед первым байтом нельзя. CX Codex +1 883установок view-transitions Учит агента работать с View Transitions API: переходы между страницами и состояниями интерфейса браузер анимирует сам. CX Codex +1 678установок vite-bundle-optimization Скилл с паттернами оптимизации бандла и сборки в Vite + React. Пригодится, когда билд тормозит или чанки выходят больше ожидаемого. CX Codex +1 667установок command-pattern Обучает агента паттерну «Команда» для JavaScript и TypeScript. Помогает разделить вызов операции и её выполнение, настроить очередь задач или историю отмены. CX Codex +1 645установок singleton-pattern Обучает реализации паттерна «Одиночка» в JavaScript и TypeScript для создания единого экземпляра класса. Пригодится при настройке общих ресурсов или глобальной конфигурации. CX Codex +1 637установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой