本文へ移動
cccskills
無料GitHub で公開

web-framework-vue-composition-api

Vue 3 Composition API — reactivity primitives, composables, lifecycle, the 3.4+ and 3.5+ macros. Load when writing Vue 3 components or composables.

インストール方法を見る

含まれるファイル(11)

  • SKILL.md12.6 KB
  • examples/async.md4.0 KB
  • examples/composables.md6.2 KB
  • examples/core.md4.2 KB
  • examples/define-expose.md3.9 KB
  • examples/lifecycle.md4.4 KB
  • examples/provide-inject.md4.4 KB
  • examples/reactivity.md4.4 KB
  • examples/vue-3-5-features.md8.5 KB
  • metadata.yaml347 B
  • reference.md11.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Vue 3 Composition API

Quick Guide: <script setup> for every component. ref() for primitives and anything reassigned, reactive() for an object mutated in place. Reusable stateful logic goes into a use* composable that returns an object of refs. Everything opened in onMounted is closed in onUnmounted. The 3.4+ and 3.5+ macros — defineModel(), useTemplateRef(), useId(), onWatcherCleanup() — replace whole patterns that preceded them, and destructured props need a getter wrapper in watch().

Detailed Resources:


Which path applies

  • Vue on its own — component data fetching goes through watch/watchEffect or an async composable, as Patterns 3 and 5 describe.
  • Vue under a meta-framework — the framework owns fetching, caching and SSR-safe hydration through its own composables; take Patterns 1, 2, 4 and 6–11 unchanged and let it handle the rest.

<critical_requirements>

Before writing Vue code

Write components as <script setup>. Bindings reach the template with no return statement, and the compiler macros — defineProps, defineEmits, defineModel — only exist inside it.

Pair every onMounted setup with an onUnmounted teardown. Timers, listeners, observers and sockets outlive the component otherwise, and nothing reports it.

Pick ref() for primitives and anything you will reassign, reactive() for an object you mutate in place. A reassigned reactive variable leaves the old proxy behind, still wired to the template.

Prefix a composable with use. The convention is what tells a reader — and the linter — that the function may call lifecycle hooks and must run during setup.

Wrap a destructured prop in a getter for watch() — watch(() => count, …). Passing the value itself hands watch a number, which it can never see change.

</critical_requirements>


Auto-detection: Vue 3 Composition API, script setup, ref, reactive, computed, watch, watchEffect, composables, onMounted, onUnmounted, defineProps, defineEmits, defineExpose, defineModel, useTemplateRef, useId, onWatcherCleanup, provide, inject, InjectionKey, Suspense, toRefs, toValue, MaybeRefOrGetter

Applies to:

  • Reactive state with ref, reactive, computed, watch and watchEffect
  • Component contracts: props, emits, exposed methods, v-model
  • Composables for reusable stateful logic
  • Lifecycle and cleanup
  • Provide/inject, async components and Suspense

Handled elsewhere:

  • Styling — a <style scoped> block is Vue's, and which CSS approach fills it is not settled here
  • Application-wide state stores that outlive a component tree
  • Server-state caching, invalidation and request deduplication
  • Routing, and the data a route loads
  • Test doubles for the network

<philosophy>

Philosophy

The Composition API groups code by the concern it serves rather than by the kind of thing it is. One feature's state, its derived values, its watcher and its cleanup sit together and can be lifted out whole into a composable — where the Options API scattered the same feature across data, computed, methods and mounted, and offered no way to move it.

That is what makes a composable the unit of reuse: it is a plain function that happens to call reactive primitives, so it composes, takes arguments and returns whatever shape suits — no mixin merge order, no name collisions.

</philosophy>
<patterns>

Core patterns

Pattern 1: Script setup, props and emits

defineProps and defineEmits take type arguments, so the contract is declared once and checked at both ends.

<script setup lang="ts">
const props = defineProps<{
  userId: string;
  initialCount?: number;
}>();

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

const count = ref(props.initialCount ?? 0);
const doubleCount = computed(() => count.value * 2);
</script>

The named-tuple emit syntax (3.3+) documents each payload in the type itself.

Full code: examples/core.md


Pattern 2: ref and reactive

const count = ref(0);
count.value++;

const state = reactive({
  user: null as User | null,
  settings: { theme: "light" },
});
state.settings.theme = "dark";

.value in script, unwrapped in template. Destructuring a reactive object copies its values out of the proxy — toRefs(state) is what keeps them connected.

Full code: examples/reactivity.md


Pattern 3: watch and watchEffect

watch names its source and gives you the previous value; watchEffect tracks whatever it reads and runs immediately. onWatcherCleanup() (3.5+) cancels work the next run supersedes.

watch(searchQuery, async (newQuery, oldQuery) => {
  /* … */
});

watchEffect(async () => {
  if (userId.value) userData.value = await fetchUser(userId.value);
});

watch(searchQuery, async (query) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  await fetch(`/api/search?q=${query}`, { signal: controller.signal });
});

A property of a reactive object is watched through a getter — watch(() => state.count, …).

Full code: examples/vue-3-5-features.md


Pattern 4: Lifecycle and cleanup

const POLL_INTERVAL_MS = 5000;
let intervalId: ReturnType<typeof setInterval> | null = null;

onMounted(() => {
  intervalId = setInterval(fetchData, POLL_INTERVAL_MS);
});

onUnmounted(() => {
  if (intervalId) {
    clearInterval(intervalId);
    intervalId = null;
  }
});

Full code: examples/lifecycle.md


Pattern 5: Composables

A composable returns an object of refs, so a caller can destructure it without flattening the reactivity.

export function useCounter(options: UseCounterOptions = {}) {
  const { initialValue = 0, max = Infinity } = options;
  const count = ref(initialValue);
  const isAtMax = computed(() => count.value >= max);

  function increment() {
    if (count.value < max) count.value++;
  }

  return { count, isAtMax, increment };
}

An async composable takes MaybeRefOrGetter<T> inputs, normalises them with toValue(), and returns { data, error, isLoading } — so a caller can pass a ref, a getter or a plain value interchangeably.

Full code: examples/composables.md


Pattern 6: defineModel for v-model (3.4+)

Returns a ref that reads the prop and emits the update, replacing the defineProps + defineEmits pair the parent used to need.

<script setup lang="ts">
const model = defineModel<string>();
const firstName = defineModel<string>("firstName");

const [value, modifiers] = defineModel<string>({
  set(v) {
    return modifiers.capitalize ? v.charAt(0).toUpperCase() + v.slice(1) : v;
  },
});
</script>

Full code: examples/vue-3-5-features.md


Pattern 7: Template refs (3.5+)

useTemplateRef() looks up a ref by its string name, which is what makes it work with a dynamic name and inside a composable. A plain ref() matching the attribute still works for a static one.

<script setup lang="ts">
const inputRef = useTemplateRef<HTMLInputElement>("myInput");
onMounted(() => inputRef.value?.focus());
</script>
<template>
  <input ref="myInput" type="text" />
</template>

For a child component, defineExpose() declares its public surface, and the parent types the ref InstanceType<typeof Child>.

Full code: examples/define-expose.md, examples/vue-3-5-features.md


Pattern 8: useId for accessible ids (3.5+)

Generates an id that matches between server and client render, which is what a hand-rolled counter or a random string cannot do.

<script setup lang="ts">
const id = useId();
</script>
<template>
  <label :for="id">Email</label>
  <input :id="id" type="email" />
</template>

Each call returns a different id, so call it once per field in setup — never inside a computed.

Full code: examples/vue-3-5-features.md


Pattern 9: Reactive props destructure (3.5+)

Destructured props stay reactive, and JavaScript default syntax replaces withDefaults().

<script setup lang="ts">
const {
  title,
  count = 0,
  items = () => [],
} = defineProps<{
  title: string;
  count?: number;
  items?: string[];
}>();

watch(
  () => count,
  (newCount) => {
    /* … */
  },
);
</script>

The compiler rewrites each reference into a prop access, so a destructured name passed as a value — to watch, or into a function — is just the value at that instant. Hence the getter.

Full code: examples/vue-3-5-features.md


Pattern 10: Provide/inject

An InjectionKey<T> symbol carries the value's type from provider to consumer, so neither side casts.

// injection-keys.ts
export const THEME_KEY: InjectionKey<ThemeContext> = Symbol("theme");

// provider
provide(THEME_KEY, { theme, toggleTheme });

// consumer
const ctx = inject(THEME_KEY);
if (!ctx) throw new Error("Must be used within ThemeProvider");

Full code: examples/provide-inject.md


Pattern 11: Async components and Suspense

defineAsyncComponent code-splits at the component boundary. A top-level await in <script setup> makes the component async, which requires a <Suspense> above it.

const LOADING_DELAY_MS = 200;
const LOAD_TIMEOUT_MS = 10000;

const HeavyChart = defineAsyncComponent({
  loader: () => import("./components/heavy-chart.vue"),
  loadingComponent: LoadingSpinner,
  delay: LOADING_DELAY_MS,
  timeout: LOAD_TIMEOUT_MS,
});

delay is what stops the spinner flashing on a fast load. Errors from the boundary are caught with onErrorCaptured.

Full code: examples/async.md

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • A prop assigned to — props.count++ warns and changes nothing, because props are read-only; emit an update and let the parent own the value
  • A destructured prop watched directly — watch(count, …) receives a number and never fires; wrap it in a getter
  • A reactive object destructured without toRefs() — the copies leave the proxy and stop updating
  • A reactive variable reassigned — the template still holds the old proxy
  • Setup opened without a matching onUnmounted — the timer, listener or socket survives the component
  • watch running an async request with no cleanup — a slow earlier response overwrites a fast later one; onWatcherCleanup() (3.5+) or the cleanup callback settles the order
  • useId() called inside a computed — it mints a new id per evaluation, so the label and the input drift apart
  • provide() with a string key — the consumer gets unknown, and two features can collide on the same key

Surprising behaviour:

  • .value written in a template is wrong; templates unwrap refs already
  • Refs nested in a reactive object unwrap at the root, but not inside an array, a Map or a Set
  • watchEffect runs immediately, watch does not until immediate: true
  • A computed is read-only unless declared with a getter and a setter
  • A provided value is not reactive on its own — wrap it in ref() or reactive() if consumers should see changes
  • onUnmounted never runs for a component that threw during setup
  • defineModel returns a ref, so it takes .value in script and none in the template

</red_flags>

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

agents-inc のスキルをすべて見る

このスキルの問題を報告する