Composition API Basic Rules
10.1.1. Fundamental Principles
<script setup>is mandatory. Options API must not be used.- TypeScript is mandatory. The
<script>tag in.vuefiles must always includelang="ts". - Block order within an SFC (Single File Component) must follow
<script>then<template>then<style>.
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>
<template>
<div>
<p>{{ doubled }}</p>
<button @click="count++">Increment</button>
</div>
</template>
<style scoped>
/* Styles */
</style>10.1.2. Composable Authoring Rules
- Composable function names must start with the
useprefix. - Files must be placed in the
src/composables/directory. - Each file must define exactly one composable.
typescript
// src/composables/useUser.ts
import { ref } from 'vue'
import { fetchUser } from '@/api/user'
import type { User } from '@/types/user'
export function useUser(userId: string) {
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
async function load() {
loading.value = true
try {
user.value = await fetchUser(userId)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
return { user, loading, error, load }
}10.1.3. ref and reactive Usage Rules
- Use
reffor primitive types (string, number, boolean). - Use
refas the default for object/array types as well.reactiveshould be used only sparingly, limited to cases where reactivity tracking is needed for deeply nested objects. - Access
refvalues explicitly via.value. In templates, they are automatically unwrapped.
typescript
// Correct
const name = ref<string>('')
const items = ref<Item[]>([])
const form = ref<FormData>({ name: '', email: '' })
// Limited use — reactive
const state = reactive<FormState>({
fields: { name: '', email: '' },
errors: {},
isSubmitting: false,
})10.1.4. Lifecycle Hooks
- Use Composition API lifecycle hooks. Options API lifecycle methods such as
createdandmountedmust not be used. - Code executed directly within
<script setup>corresponds tocreated/beforeCreate. No separate hook is needed.
| Options API | Composition API | Purpose |
|---|---|---|
mounted | onMounted | DOM access, external library initialization |
unmounted | onUnmounted | Event listener removal, timer cleanup |
updated | onUpdated | Post-DOM-update processing (minimize use) |
beforeUnmount | onBeforeUnmount | Begin cleanup operations |
typescript
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})