Claude-skill-registry Comprehensive Debugging
Master debugging techniques for Vue.js, Pinia, and cached state issues. Identify reactivity problems, memory leaks, performance bottlenecks, and complex state synchronization issues with systematic troubleshooting approaches.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-debugging-endlessblink-flow-state" ~/.claude/skills/majiayu000-claude-skill-registry-comprehensive-debugging && rm -rf "$T"
manifest:
skills/data/dev-debugging-endlessblink-flow-state/SKILL.mdsource content
Comprehensive Debugging
Instructions
Debugging Philosophy
Always follow systematic debugging methodology:
- Identify the Problem: Gather specific symptoms and error messages
- Isolate the Issue: Use binary search to narrow down the root cause
- Verify the Fix: Ensure the solution works and doesn't introduce regressions
- Document the Solution: Create reusable debugging patterns
Vue.js Reactivity Debugging
Common Reactivity Issues
- Direct Property Assignment: Adding properties to reactive objects without
orVue.set()reactive() - Array Mutation: Direct index assignment or length modification without proper methods
- Computed Caching: Computed properties not updating due to stale dependencies
- Watch Invalidation: Watchers not firing due to incorrect configuration
Debugging Tools
import { watch, watchEffect, computed, ref, reactive } from 'vue' // Debug reactivity dependencies const debugReactivity = () => { const state = reactive({ count: 0, nested: { value: 0 } }) // Track what triggers reactivity watchEffect(() => { console.log('Effect triggered with:', state.count, state.nested.value) }) // Debug computed dependencies const doubled = computed(() => { console.log('Computed recalculating with:', state.count) return state.count * 2 }) return { state, doubled } } // Component-level debugging hooks export default { setup() { // Debug what triggers re-renders onRenderTracked((e) => { console.log('Render tracked:', e.key, e.type, e.target) }) onRenderTriggered((e) => { console.log('Render triggered:', e.key, e.type, e.target) }) } }
Pinia State Debugging
Store State Issues
- Stale State: State not updating due to missing reactivity
- Persistence Conflicts: LocalStorage hydration overriding current state
- Action Side Effects: Actions causing unexpected state mutations
- Subscription Leaks: Unmanaged store subscriptions
Debugging Patterns
import { defineStore } from 'pinia' export const useDebugStore = defineStore('debug', { state: () => ({ tasks: [], selectedTaskId: null, lastMutation: null, debugMode: true }), actions: { // Add mutation tracking addTask(task) { if (this.debugMode) { console.trace('addTask called with:', task) console.log('State before:', JSON.parse(JSON.stringify(this.tasks))) } this.tasks.push(task) this.lastMutation = { type: 'addTask', timestamp: Date.now(), payload: task } if (this.debugMode) { console.log('State after:', JSON.parse(JSON.stringify(this.tasks))) } }, // Batch mutations for atomic updates updateTasks(updates) { this.$patch((state) => { updates.forEach(({ id, ...changes }) => { const task = state.tasks.find(t => t.id === id) if (task) { Object.assign(task, changes) if (this.debugMode) { console.log(`Task ${id} updated:`, changes) } } }) }) } }, // Debug getters with caching issues getters: { completedTasks: (state) => { console.log('completedTasks getter recalculating') return state.tasks.filter(task => task.completed) }, taskById: (state) => { console.log('taskById getter recalculating') return (id) => state.tasks.find(task => task.id === id) } } })
Cached State Issues
Browser Storage Debugging
import { useLocalStorage, useSessionStorage } from '@vueuse/core' const debugStorage = () => { const settings = useLocalStorage('app-settings', { theme: 'light', language: 'en' }) // Monitor storage changes window.addEventListener('storage', (e) => { console.log('Storage changed:', e.key, e.oldValue, e.newValue) }) // Debug hydration conflicts const checkStorageConsistency = () => { const raw = localStorage.getItem('app-settings') const parsed = JSON.parse(raw) console.log('Raw storage:', raw) console.log('Parsed storage:', parsed) console.log('Current reactive:', settings.value) if (JSON.stringify(settings.value) !== JSON.stringify(parsed)) { console.warn('Storage inconsistency detected!') } } return { settings, checkStorageConsistency } }
Memory Leak Detection
const detectMemoryLeaks = () => { const componentInstances = new WeakSet() const subscriptions = new Set() const trackComponent = (component) => { componentInstances.add(component) console.log('Component tracked:', component.$options.name) } const trackSubscription = (unsubscribe) => { subscriptions.add(unsubscribe) // Auto-cleanup after component unmounts onUnmounted(() => { if (subscriptions.has(unsubscribe)) { unsubscribe() subscriptions.delete(unsubscribe) console.log('Subscription cleaned up') } }) } // Monitor for memory leaks setInterval(() => { console.log('Active subscriptions:', subscriptions.size) console.log('Tracked components:', componentInstances.size || 0) }, 10000) return { trackComponent, trackSubscription } }
Performance Debugging
Component Performance
<template> <div> <!-- Use v-memo for expensive rendering --> <ExpensiveComponent v-for="item in items" :key="item.id" v-memo="[item.id, item.lastModified]" :data="item" /> </div> </template> <script setup> import { ref, computed, shallowRef } from 'vue' // Performance monitoring const renderCount = ref(0) const lastRenderTime = ref(Date.now()) // Use shallowRef for large arrays const items = shallowRef([]) // Optimize computed properties const expensiveComputed = computed(() => { renderCount.value++ lastRenderTime.value = Date.now() console.log(`Computed recalculated (${renderCount.value} times)`) return items.value .filter(item => item.active) .map(item => processItem(item)) }) // Debug performance onMounted(() => { const observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { if (entry.entryType === 'measure') { console.log(`Performance: ${entry.name} took ${entry.duration}ms`) } }) }) observer.observe({ entryTypes: ['measure'] }) // Measure render performance performance.mark('render-start') nextTick(() => { performance.mark('render-end') performance.measure('render-time', 'render-start', 'render-end') }) }) </script>
Canvas Debugging
Canvas State Issues
const debugCanvas = () => { const canvasState = reactive({ nodes: [], selectedNodes: [], viewport: { x: 0, y: 0, zoom: 1 }, isDragging: false }) // Debug canvas mutations const debugCanvasMutation = (action, data) => { console.group(`Canvas ${action}`) console.log('State before:', JSON.parse(JSON.stringify(canvasState))) console.log('Mutation data:', data) action(canvasState, data) console.log('State after:', JSON.parse(JSON.stringify(canvasState))) console.groupEnd() } // Debug selection issues const debugSelection = (nodeIds) => { console.log('Selecting nodes:', nodeIds) console.log('Current selection:', canvasState.selectedNodes) console.log('Available nodes:', canvasState.nodes.map(n => n.id)) const invalidIds = nodeIds.filter(id => !canvasState.nodes.some(n => n.id === id) ) if (invalidIds.length > 0) { console.warn('Invalid node IDs in selection:', invalidIds) } } return { canvasState, debugCanvasMutation, debugSelection } }
Async Operation Debugging
Promise and Timer Debugging
const debugAsync = () => { const activePromises = new Set() const activeTimers = new Set() const debugPromise = (promise, description) => { activePromises.add(promise) console.log(`Promise started: ${description}`) promise .then(result => { console.log(`Promise resolved: ${description}`, result) activePromises.delete(promise) }) .catch(error => { console.error(`Promise rejected: ${description}`, error) activePromises.delete(promise) }) return promise } const debugTimer = (callback, delay, description) => { const timerId = setTimeout(() => { console.log(`Timer fired: ${description}`) callback() activeTimers.delete(timerId) }, delay) activeTimers.add(timerId) console.log(`Timer set: ${description} (${delay}ms)`) return timerId } // Check for leaks setInterval(() => { console.log('Active promises:', activePromises.size) console.log('Active timers:', activeTimers.size) if (activePromises.size > 10) { console.warn('Possible promise leak detected!') } if (activeTimers.size > 5) { console.warn('Possible timer leak detected!') } }, 5000) return { debugPromise, debugTimer } }
Error Boundary Debugging
Global Error Handling
const setupErrorHandling = () => { // Vue error handler app.config.errorHandler = (err, instance, info) => { console.group('Vue Error') console.error('Error:', err) console.error('Component:', instance?.$options.name) console.error('Info:', info) console.error('Props:', instance?.$props) console.error('State:', instance?.$data) console.groupEnd() // Send to error tracking service errorTracking.captureException(err, { tags: { component: instance?.$options.name }, extra: { info, props: instance?.$props } }) } // Unhandled promise rejections window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise rejection:', event.reason) // Prevent default browser behavior event.preventDefault() }) // Global error tracking window.addEventListener('error', (event) => { console.error('Global error:', event.error) }) }
Network Debugging
API Request Debugging
const debugNetwork = () => { const originalFetch = window.fetch window.fetch = async (...args) => { const [url, options = {}] = args const requestId = Math.random().toString(36).substr(2, 9) console.group(`🌐 Request ${requestId}: ${options.method || 'GET'} ${url}`) console.log('Request:', options) const startTime = performance.now() try { const response = await originalFetch(...args) const duration = performance.now() - startTime console.log(`✅ Response ${requestId}: ${response.status} (${duration.toFixed(2)}ms)`) console.log('Headers:', Object.fromEntries(response.headers.entries())) // Clone response to avoid consuming it const clonedResponse = response.clone() const data = await clonedResponse.json().catch(() => clonedResponse.text()) console.log('Data:', data) console.groupEnd() return response } catch (error) { const duration = performance.now() - startTime console.error(`❌ Error ${requestId}: ${error.message} (${duration.toFixed(2)}ms)`) console.groupEnd() throw error } } }
Debugging Utilities
State Snapshot
const createDebugger = () => { const snapshots = [] const maxSnapshots = 10 const takeSnapshot = (label, data) => { const snapshot = { label, timestamp: Date.now(), data: JSON.parse(JSON.stringify(data)), stackTrace: new Error().stack } snapshots.push(snapshot) if (snapshots.length > maxSnapshots) { snapshots.shift() } console.log(`📸 Snapshot: ${label}`, snapshot) } const compareSnapshots = (index1, index2) => { const snap1 = snapshots[index1] const snap2 = snapshots[index2] const diff = findDiff(snap1.data, snap2.data) console.log(`🔍 Comparing snapshots: ${snap1.label} vs ${snap2.label}`) console.log('Differences:', diff) } const findDiff = (obj1, obj2) => { // Simple diff implementation const diffs = [] const keys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]) for (const key of keys) { if (JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) { diffs.push({ key, oldValue: obj1[key], newValue: obj2[key] }) } } return diffs } return { takeSnapshot, compareSnapshots, snapshots } }
Usage Examples
Quick Debug Patterns
// Quick reactivity check const checkReactivity = (obj, property) => { const value = obj[property] obj[property] = value console.log('Reactivity check:', obj[property] === value) } // Performance measurement const measurePerformance = (fn, label) => { const start = performance.now() const result = fn() const end = performance.now() console.log(`⏱️ ${label}: ${(end - start).toFixed(2)}ms`) return result } // Debug store mutations const debugStoreMutations = (store) => { store.$subscribe((mutation, state) => { console.group(`🔄 Store Mutation: ${mutation.type}`) console.log('Payload:', mutation.payload) console.log('New state:', state) console.groupEnd() }) }
This comprehensive debugging skill provides systematic approaches to identify and resolve complex state management, reactivity, and performance issues in Vue.js applications with Pinia stores.