Composition API组件写法
Composition API 是 Vue 3 引入的一种新的组件编写方式,它通过逻辑关注点组织代码,解决了 Options API 在复杂组件中代码分散的问题。下面从核心概念、实际应用和对比分析展开具体说明。
核心概念与基础用法
Composition API 的核心是 setup()
函数,它是组合式逻辑的入口点。与 Options API 不同,所有响应式数据、计算属性和方法都集中在这里声明:
<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>
响应式数据通过 ref()
或 reactive()
创建:
ref
用于基本类型,通过.value
访问reactive
用于对象类型,可直接访问属性
逻辑复用与组合函数
组合函数 (Composable) 是复用逻辑的主要方式。典型的组合函数封装特定功能并返回响应式数据:
// 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 }
}
组件中使用组合函数:
<script setup>
import { useCounter } from './useCounter'
const { count, increment } = useCounter(10)
</script>
生命周期钩子的使用
Composition API 提供了对应的生命周期钩子函数,前缀为 on
:
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('组件挂载')
const timer = setInterval(() => {
console.log('执行定时任务')
}, 1000)
onUnmounted(() => {
clearInterval(timer)
})
})
</script>
与 Options API 的对比
- 代码组织方式
// 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 支持 Composition API 天然支持类型推断:
interface User {
id: number
name: string
}
const user = ref<User>({ id: 1, name: 'Alice' })
高级模式与最佳实践
- Props 处理
<script setup>
const props = defineProps({
title: {
type: String,
required: true
},
likes: Number
})
// TypeScript 版本
const props = defineProps<{
title: string
likes?: number
}>()
</script>
- 状态管理集成
// 使用 Pinia
import { useStore } from '@/stores/counter'
const store = useStore()
// 直接修改状态
store.increment()
// 使用计算属性
const double = computed(() => store.doubleCount)
- 异步操作处理
<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
}
}
// 立即执行
fetchData()
</script>
常见问题解决方案
- 响应式丢失问题
// 错误做法:解构会失去响应性
const { x, y } = reactive({ x: 1, y: 2 })
// 正确做法:使用 toRefs
const state = reactive({ x: 1, y: 2 })
const { x, y } = toRefs(state)
- 模板引用
<template>
<input ref="inputRef">
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
</script>
- 依赖注入
// 祖先组件
import { provide } from 'vue'
provide('theme', 'dark')
// 后代组件
import { inject } from 'vue'
const theme = inject('theme', 'light') // 默认值
性能优化技巧
- 计算属性缓存
const expensiveValue = computed(() => {
// 复杂计算
return heavyCalculation(items.value)
})
- watch 优化
// 只监听特定属性
watch(
() => obj.deep.nested.value,
(newVal) => {
console.log('变化:', newVal)
},
{ immediate: true }
)
// 批量监听
watch([fooRef, barRef], ([foo, bar]) => {
// 当 foo 或 bar 变化时执行
})
- 浅层响应式
const shallowObj = shallowReactive({
nested: { value: 1 } // 嵌套对象不会自动转为响应式
})
与第三方库集成
- 图表库示例
<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
const chartInstance = ref(null)
const options = ref({ /* 图表配置 */ })
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>
- 动画库示例
<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>
企业级应用架构
- 分层架构示例
src/
├── composables/
│ ├── useApi.ts
│ ├── useFormValidation.ts
│ └── usePagination.ts
├── components/
│ ├── BasePagination.vue
│ └── BaseModal.vue
└── views/
├── UserList.vue
└── UserDetail.vue
- 全局状态管理
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
users: [],
currentUser: null
}),
actions: {
async fetchUsers() {
this.users = await api.getUsers()
}
}
})
- 路由控制
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function navigateToUser(id) {
router.push(`/user/${id}`)
}
</script>
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn