Composition API component writing style
The Composition API is a new component writing approach introduced in Vue 3, which organizes code by logical concerns, addressing the issue of scattered code in complex components with the Options API. Below is a detailed explanation covering core concepts, practical applications, and comparative analysis.
Core Concepts and Basic Usage
The core of the Composition API is the setup()
function, which serves as the entry point for compositional logic. Unlike the Options API, all reactive data, computed properties, and methods are declared here:
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">{{ count }} x2 = {{ doubleCount }}</button>
</template>
Reactive data is created using ref()
or reactive()
:
ref
is used for primitive types, accessed via.value
reactive
is used for object types, with direct property access
Logic Reuse and Composable Functions
Composable functions are the primary way to reuse logic. A typical composable encapsulates specific functionality and returns reactive data:
// useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment(step = 1) {
count.value += step
}
return { count, increment }
}
Using composables in components:
<script setup>
import { useCounter } from './useCounter'
const { count, increment } = useCounter(10)
</script>
Lifecycle Hook Usage
The Composition API provides corresponding lifecycle hook functions prefixed with on
:
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('Component mounted')
const timer = setInterval(() => {
console.log('Executing timer task')
}, 1000)
onUnmounted(() => {
clearInterval(timer)
})
})
</script>
Comparison with Options API
- Code Organization
// Options API
export default {
data() {
return { count: 0 }
},
methods: {
increment() { this.count++ }
},
computed: {
double() { return this.count * 2 }
}
}
// Composition API
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
- TypeScript Support The Composition API naturally supports type inference:
interface User {
id: number
name: string
}
const user = ref<User>({ id: 1, name: 'Alice' })
Advanced Patterns and Best Practices
- Props Handling
<script setup>
const props = defineProps({
title: {
type: String,
required: true
},
likes: Number
})
// TypeScript version
const props = defineProps<{
title: string
likes?: number
}>()
</script>
- State Management Integration
// Using Pinia
import { useStore } from '@/stores/counter'
const store = useStore()
// Direct state modification
store.increment()
// Using computed properties
const double = computed(() => store.doubleCount)
- Async Operations Handling
<script setup>
import { ref } from 'vue'
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function fetchData() {
loading.value = true
try {
const response = await fetch('/api/data')
data.value = await response.json()
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
// Immediate execution
fetchData()
</script>
Common Problem Solutions
- Reactivity Loss Issue
// Wrong approach: Destructuring loses reactivity
const { x, y } = reactive({ x: 1, y: 2 })
// Correct approach: Use toRefs
const state = reactive({ x: 1, y: 2 })
const { x, y } = toRefs(state)
- Template Refs
<template>
<input ref="inputRef">
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
</script>
- Dependency Injection
// Ancestor component
import { provide } from 'vue'
provide('theme', 'dark')
// Descendant component
import { inject } from 'vue'
const theme = inject('theme', 'light') // Default value
Performance Optimization Techniques
- Computed Property Caching
const expensiveValue = computed(() => {
// Complex calculation
return heavyCalculation(items.value)
})
- watch Optimization
// Watch specific properties only
watch(
() => obj.deep.nested.value,
(newVal) => {
console.log('Change:', newVal)
},
{ immediate: true }
)
// Batch watching
watch([fooRef, barRef], ([foo, bar]) => {
// Executes when foo or bar changes
})
- Shallow Reactivity
const shallowObj = shallowReactive({
nested: { value: 1 } // Nested objects are not automatically reactive
})
Integration with Third-Party Libraries
- Chart Library Example
<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
const chartInstance = ref(null)
const options = ref({ /* Chart configuration */ })
onMounted(() => {
chartInstance.value = echarts.init(chartRef.value)
chartInstance.value.setOption(options.value)
})
watch(options, (newVal) => {
chartInstance.value?.setOption(newVal)
}, { deep: true })
</script>
<template>
<div ref="chartRef" style="width: 600px; height: 400px;"></div>
</template>
- Animation Library Example
<script setup>
import { ref } from 'vue'
import gsap from 'gsap'
const boxRef = ref(null)
function animate() {
gsap.to(boxRef.value, {
x: 100,
duration: 1,
ease: 'power2.out'
})
}
</script>
<template>
<div ref="boxRef" @click="animate"></div>
</template>
Enterprise-Level Application Architecture
- Layered Architecture Example
src/
├── composables/
│ ├── useApi.ts
│ ├── useFormValidation.ts
│ └── usePagination.ts
├── components/
│ ├── BasePagination.vue
│ └── BaseModal.vue
└── views/
├── UserList.vue
└── UserDetail.vue
- Global State Management
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
users: [],
currentUser: null
}),
actions: {
async fetchUsers() {
this.users = await api.getUsers()
}
}
})
- Route Control
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function navigateToUser(id) {
router.push(`/user/${id}`)
}
</script>
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn