vue-composition-api

安装量: 43
排名: #16895

安装

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

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

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([]);

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

Advanced Composable with Side Effects // composables/useFetch.ts import { ref, unref, watchEffect } from 'vue'; import type { Ref } from 'vue';

export function useFetch(url: Ref | string) { const data = ref(null); const error = ref(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

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

Props and Emits in Composition API TypeScript Props

TypeScript Emits

Runtime Props Validation

Provide and Inject Patterns Basic Provide/Inject

Type-Safe Provide/Inject // keys.ts import type { InjectionKey, Ref } from 'vue';

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

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

// Provider

// Consumer

Provide with Default Values

TypeScript with Composition API Component with Full Types

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

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

// 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', { 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 Prefer

Async Data Loading

Resources Vue 3 Composition API Documentation Composition API RFC VueUse - Collection of Composables Vue 3 TypeScript Guide Composables Best Practices

返回排行榜