Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问clear审计通过

vue-composition-apiVue composition API 文档

Agent Skill

用于辅助 API 设计、接口文档、请求响应结构和服务集成说明。它适合让 Agent 梳理 endpoint、生成 OpenAPI 草稿、检查字段命名、整理错误码或辅助前后端联调。使用时需要确认真实业务语义、鉴权方式、分页和错误处理规则;涉及生成接口文档时,应避免凭空补字段,最好从现有代码、schema 或接口样例中提取事实。

总安装

1,473

周安装

62

GitHub Stars

142

下载量

516
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:vue-composition-api(Vue composition API 文档)
来源仓库:https://github.com/thebushidocollective/han
仓库路径:skills/vue-composition-api
安装命令:
npx skills add https://github.com/thebushidocollective/han --skill vue-composition-api
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/thebushidocollective/han --skill vue-composition-api

简介

用于 Vue Composition API 的接口设计与文档生成。

  • 适合梳理组件 props、emits 和响应式数据定义。
  • 需从现有代码或 schema 中提取真实字段语义。
  • 生成的文档应避免虚构未实现的 endpoint 或参数。
  • 安装后建议对照实际业务逻辑复核输出内容。vue-composition-api 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

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:

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:

<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

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

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

// 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

import { ref, computed } from 'vue';

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

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

Writable Computed

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

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

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

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

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

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

// 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

// 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.value = e as Error;
    } finally {
      loading.value = false;
    }
  }

  watchEffect(() => {
    fetchData();
  });

  return {
    data: readonly(data),
    error: readonly(error),
    loading: readonly(loading),
    refetch: fetchData
  };
}

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

const userId = ref('1');
const url = computed(() => `/api/users/${userId.value}`);
const { data, error, loading, refetch } = useFetch(url);
</script>

Composable with Cleanup

// composables/useEventListener.ts
import { onMounted, onUnmounted } from 'vue';

export function useEventListener(
  target: EventTarget,
  event: string,
  handler: (e: Event) => void
) {
  onMounted(() => {
    target.addEventListener(event, handler);
  });

  onUnmounted(() => {
    target.removeEventListener(event, handler);
  });
}

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

useEventListener(window, 'resize', () => {
  console.log('Window resized');
});
</script>

Props and Emits in Composition API

TypeScript Props

<script setup lang="ts">
interface Props {
  title: string;
  count?: number;
  items: string[];
  user: {
    name: string;
    email: string;
  };
}

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

// Access props
console.log(props.title);
console.log(props.count);

// Destructuring loses reactivity - use toRefs
import { toRefs } from 'vue';
const { title, count } = toRefs(props);
</script>

TypeScript Emits

<script setup lang="ts">
// Type-safe emits
const emit = defineEmits<{
  update: [value: number];
  delete: [];
  change: [id: string, value: string];
}>();

function handleUpdate() {
  emit('update', 42);
}

function handleChange(id: string, value: string) {
  emit('change', id, value);
}
</script>

Runtime Props Validation

<script setup lang="ts">
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0,
    validator: (value: number) => value >= 0
  },
  status: {
    type: String as PropType<'active' | 'inactive'>,
    default: 'active'
  }
});
</script>

Provide and Inject Patterns

Basic Provide/Inject

<!-- Parent Component -->
<script setup lang="ts">
import { provide, ref } from 'vue';

const theme = ref('dark');
const updateTheme = (newTheme: string) => {
  theme.value = newTheme;
};

provide('theme', { theme, updateTheme });
</script>

<!-- Child Component (any depth) -->
<script setup lang="ts">
import { inject } from 'vue';

const themeContext = inject('theme');
// themeContext.theme
// themeContext.updateTheme('light')
</script>

Type-Safe Provide/Inject

// keys.ts
import type { InjectionKey, Ref } from 'vue';

export interface ThemeContext {
  theme: Ref<string>;
  updateTheme: (theme: string) => void;
}

export const ThemeKey: InjectionKey<ThemeContext> =
  Symbol('theme');

// Provider
<script setup lang="ts">
import { provide, ref } from 'vue';
import { ThemeKey } from './keys';

const theme = ref('dark');
const updateTheme = (newTheme: string) => {
  theme.value = newTheme;
};

provide(ThemeKey, { theme, updateTheme });
</script>

// Consumer
<script setup lang="ts">
import { inject } from 'vue';
import { ThemeKey } from './keys';

const theme = inject(ThemeKey);
// Fully typed!
</script>

Provide with Default Values

<script setup lang="ts">
import { inject } from 'vue';

const theme = inject('theme', {
  theme: ref('light'),
  updateTheme: () => {}
});

// Or use factory function for reactive defaults
const config = inject('config', () => reactive({
  locale: 'en',
  timezone: 'UTC'
}), true); // true = treat as factory
</script>

TypeScript with Composition API

Component with Full Types

<script setup lang="ts">
import { ref, computed, type Ref, type ComputedRef } from 'vue';

interface User {
  id: number;
  name: string;
  email: string;
}

interface Props {
  userId: number;
}

interface Emits {
  (e: 'update', user: User): void;
  (e: 'delete', id: number): void;
}

const props = defineProps<Props>();
const emit = defineEmits<Emits>();

const user: Ref<User | null> = ref(null);
const isLoading = ref(false);

const userName: ComputedRef<string> = computed(() =>
  user.value?.name ?? 'Unknown'
);

async function loadUser() {
  isLoading.value = true;
  try {
    const response = await fetch(`/api/users/${props.userId}`);
    user.value = await response.json();
  } finally {
    isLoading.value = false;
  }
}

function updateUser(updates: Partial<User>) {
  if (user.value) {
    user.value = { ...user.value, ...updates };
    emit('update', user.value);
  }
}
</script>

Generic Composables

// composables/useLocalStorage.ts
import { ref, watch, type Ref } from 'vue';

export function useLocalStorage<T>(
  key: string,
  defaultValue: T
): Ref<T> {
  const data = ref<T>(defaultValue) as Ref<T>;

  // Load from localStorage
  const stored = localStorage.getItem(key);
  if (stored) {
    try {
      data.value = JSON.parse(stored);
    } catch (e) {
      console.error('Failed to parse localStorage', e);
    }
  }

  // Save to localStorage on change
  watch(
    data,
    (newValue) => {
      localStorage.setItem(key, JSON.stringify(newValue));
    },
    { deep: true }
  );

  return data;
}

// Usage
const user = useLocalStorage<User>('user', { id: 0, name: '' });

When to Use This Skill

Use vue-composition-api when building modern, production-ready applications that require:

  • Complex component logic that benefits from better organization
  • Reusable logic across multiple components (composables)
  • Better TypeScript integration and type inference
  • Fine-grained reactivity control
  • Large-scale applications requiring maintainability
  • Migration from Vue 2 Options API to Vue 3
  • Sharing stateful logic without mixins

Vue-Specific Best Practices

  1. Prefer <script setup> syntax - Cleaner, better performance, better types
  2. Use composables for reusable logic - Extract to composables/ directory
  3. Use ref for primitives, reactive for objects - Unless you need to replace objects
  4. Always use TypeScript - Better DX and fewer runtime errors
  5. Destructure reactive objects with toRefs - Preserve reactivity
  6. Use computed for derived state - Not methods in templates
  7. Cleanup side effects - Use onUnmounted for event listeners, timers
  8. Keep components focused - Extract complex logic to composables
  9. Use provide/inject for deep prop passing - Avoid prop drilling
  10. Name composables with use prefix - Follow convention (useCounter, useFetch)

Vue-Specific Pitfalls

  1. Destructuring props directly - Loses reactivity, use toRefs(props)
  2. Forgetting .value on refs - Common source of bugs
  3. Mutating props - Props are readonly, emit events instead
  4. Using reactive() for entire state - Can't replace, use ref for root
  5. Not cleaning up watchers - Memory leaks, store stop handle
  6. Accessing refs before mount - DOM refs are null in setup
  7. Overusing reactive() - Use ref for simple values
  8. Not using computed for derived state - Recalculates on every render
  9. Forgetting to return from setup() - Without <script setup>
  10. Mixing Options API and Composition API - Confusing, pick one

Common Patterns

Form Handling

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

interface FormData {
  name: string;
  email: string;
  password: string;
}

interface FormErrors {
  name?: string;
  email?: string;
  password?: string;
}

const form = reactive<FormData>({
  name: '',
  email: '',
  password: ''
});

const errors = reactive<FormErrors>({});

const isValid = computed(() =>
  Object.keys(errors).length === 0 &&
  form.name && form.email && form.password
);

function validateEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function validate() {
  if (!form.name) {
    errors.name = 'Name is required';
  } else {
    delete errors.name;
  }

  if (!validateEmail(form.email)) {
    errors.email = 'Invalid email';
  } else {
    delete errors.email;
  }

  if (form.password.length < 8) {
    errors.password = 'Password must be 8+ characters';
  } else {
    delete errors.password;
  }
}

async function submit() {
  validate();
  if (!isValid.value) return;

  // Submit form
  await fetch('/api/register', {
    method: 'POST',
    body: JSON.stringify(form)
  });
}
</script>

Async Data Loading

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

interface Data {
  id: number;
  title: string;
}

const data = ref<Data[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);

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

  try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error('Failed to fetch');
    data.value = await response.json();
  } catch (e) {
    error.value = (e as Error).message;
  } finally {
    loading.value = false;
  }
}

onMounted(() => {
  fetchData();
});
</script>

<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">Error: {{ error }}</div>
    <div v-else>
      <div v-for="item in data" :key="item.id">
        {{ item.title }}
      </div>
    </div>
  </div>
</template>

Resources

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

29.31%
按下载量换算151

OpenCode

25.62%
按下载量换算132

Antigravity

18.61%
按下载量换算96

Gemini CLI

13.66%
按下载量换算70

Codex

7.77%
按下载量换算40

github-copilot

3.23%
按下载量换算17

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills