Claude
Skills
Sign in
Back

vue-composition-api

Included with Lifetime
$97 forever

Use when Vue 3 Composition API with reactive refs, computed, and composables. Use when building modern Vue 3 applications.

Web Dev

What this skill does


# Vue Composition API

Master the Vue 3 Composition API for building scalable, maintainable
Vue applications with better code organization and reusability.

## Setup Function Fundamentals

The `setup()` function is the entry point for using the Composition API:

```typescript
import { ref, computed, onMounted } from 'vue';

export default {
  props: ['initialCount'],
  setup(props, context) {
    // props is reactive
    console.log(props.initialCount);

    // context provides attrs, slots, emit, expose
    const { attrs, slots, emit, expose } = context;

    const count = ref(0);
    const doubled = computed(() => count.value * 2);

    function increment() {
      count.value++;
      emit('update', count.value);
    }

    onMounted(() => {
      console.log('Component mounted');
    });

    // Expose public methods
    expose({ increment });

    // Return values to template
    return {
      count,
      doubled,
      increment
    };
  }
};
```

## Script Setup Syntax

Modern Vue 3 uses `<script setup>` for cleaner syntax:

```typescript
<script setup lang="ts">
import { ref, computed } from 'vue';

// Top-level bindings automatically exposed to template
const count = ref(0);
const doubled = computed(() => count.value * 2);

function increment() {
  count.value++;
}

// Props and emits use compiler macros
interface Props {
  initialCount?: number;
}

const props = withDefaults(defineProps<Props>(), {
  initialCount: 0
});

const emit = defineEmits<{
  update: [value: number];
}>();
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Doubled: {{ doubled }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>
```

## Ref vs Reactive - When to Use Each

### Use Ref For

```typescript
import { ref } from 'vue';

// Primitives
const count = ref(0);
const name = ref('John');
const isActive = ref(true);

// Single object that needs replacement
const user = ref({ name: 'John', age: 30 });
user.value = { name: 'Jane', age: 25 }; // Works

// Arrays that need replacement
const items = ref([1, 2, 3]);
items.value = [4, 5, 6]; // Works
```

### Use Reactive For

```typescript
import { reactive, toRefs } from 'vue';

// Complex nested objects
const state = reactive({
  user: { name: 'John', age: 30 },
  settings: { theme: 'dark', notifications: true },
  posts: []
});

// Group related state
const formState = reactive({
  name: '',
  email: '',
  password: '',
  errors: {}
});

// Convert to refs for destructuring
const { name, email } = toRefs(formState);
```

### Avoid Reactive For

```typescript
// DON'T: Replacing entire reactive object loses reactivity
let state = reactive({ count: 0 });
state = reactive({ count: 1 }); // Breaks reactivity!

// DO: Use ref instead
const state = ref({ count: 0 });
state.value = { count: 1 }; // Works
```

## Computed Properties Patterns

### Basic Computed

```typescript
import { ref, computed } from 'vue';

const firstName = ref('John');
const lastName = ref('Doe');

const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`;
});
```

### Writable Computed

```typescript
const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`;
  },
  set(value) {
    const names = value.split(' ');
    firstName.value = names[0] || '';
    lastName.value = names[1] || '';
  }
});

// Can now set
fullName.value = 'Jane Smith';
```

### Computed with Complex Logic

```typescript
interface Product {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

const cart = ref<Product[]>([]);

const cartSummary = computed(() => {
  const total = cart.value.reduce((sum, item) =>
    sum + (item.price * item.quantity), 0
  );

  const itemCount = cart.value.reduce((sum, item) =>
    sum + item.quantity, 0
  );

  const tax = total * 0.08;
  const grandTotal = total + tax;

  return {
    total,
    itemCount,
    tax,
    grandTotal
  };
});
```

## Watch and WatchEffect

### Watch - Explicit Dependencies

```typescript
import { ref, watch } from 'vue';

const count = ref(0);
const name = ref('');

// Watch single source
watch(count, (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});

// Watch multiple sources
watch(
  [count, name],
  ([newCount, newName], [oldCount, oldName]) => {
    console.log('Multiple values changed');
  }
);

// Watch reactive object property
const user = reactive({ name: 'John', age: 30 });

watch(
  () => user.name,
  (newName) => {
    console.log(`Name changed to ${newName}`);
  }
);

// Deep watch
watch(
  user,
  (newUser) => {
    console.log('User changed:', newUser);
  },
  { deep: true }
);
```

### WatchEffect - Auto Tracking

```typescript
import { ref, watchEffect } from 'vue';

const count = ref(0);
const multiplier = ref(2);

// Automatically tracks dependencies
watchEffect(() => {
  console.log(`Result: ${count.value * multiplier.value}`);
});

// Runs immediately and whenever dependencies change
```

### Advanced Watch Options

```typescript
const data = ref(null);

watch(
  source,
  (newValue, oldValue) => {
    // Callback logic
  },
  {
    immediate: true,      // Run immediately
    deep: true,           // Deep watch objects
    flush: 'post',        // Timing: 'pre' | 'post' | 'sync'
    onTrack(e) {          // Debug
      console.log('tracked', e);
    },
    onTrigger(e) {        // Debug
      console.log('triggered', e);
    }
  }
);

// Stop watching
const stop = watch(source, callback);
stop(); // Cleanup
```

## Lifecycle Hooks in Composition API

```typescript
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onActivated,
  onDeactivated
} from 'vue';

export default {
  setup() {
    onBeforeMount(() => {
      console.log('Before mount');
    });

    onMounted(() => {
      console.log('Mounted');
      // DOM is available
      // Setup event listeners, fetch data
    });

    onBeforeUpdate(() => {
      console.log('Before update');
    });

    onUpdated(() => {
      console.log('Updated');
      // DOM has been updated
    });

    onBeforeUnmount(() => {
      console.log('Before unmount');
      // Cleanup before unmount
    });

    onUnmounted(() => {
      console.log('Unmounted');
      // Final cleanup
    });

    onErrorCaptured((err, instance, info) => {
      console.error('Error captured:', err, info);
      return false; // Stop propagation
    });

    // For components wrapped in <KeepAlive>
    onActivated(() => {
      console.log('Component activated');
    });

    onDeactivated(() => {
      console.log('Component deactivated');
    });
  }
};
```

## Composables - Reusable Composition Functions

### Simple Composable

```typescript
// composables/useCounter.ts
import { ref, computed } from 'vue';

export function useCounter(initialValue = 0) {
  const count = ref(initialValue);
  const doubled = computed(() => count.value * 2);

  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  function reset() {
    count.value = initialValue;
  }

  return {
    count: readonly(count),
    doubled,
    increment,
    decrement,
    reset
  };
}

// Usage
<script setup lang="ts">
import { useCounter } from '@/composables/useCounter';

const { count, doubled, increment, decrement } = useCounter(10);
</script>
```

### Advanced Composable with Side Effects

```typescript
// composables/useFetch.ts
import { ref, unref, watchEffect } from 'vue';
import type { Ref } from 'vue';

export function useFetch<T>(url: Ref<string> | string) {
  const data = ref<T | null>(null);
  const error = ref<Error | null>(null);
  const loading = ref(false);

  async function fetchData() {
    loading.value = true;
    error.value = null;

    try {
      const response = await fetch(unref(url));
      if (!response.ok) throw new Error('Fetch failed');
      data.value = await response.json();
    } catch (e) {
      error

Related in Web Dev