阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > Composition API组件写法

Composition API组件写法

作者:陈川 阅读数:16077人阅读 分类: Vue.js

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 的对比

  1. 代码组织方式
// 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++ }
  1. TypeScript 支持 Composition API 天然支持类型推断:
interface User {
  id: number
  name: string
}

const user = ref<User>({ id: 1, name: 'Alice' })

高级模式与最佳实践

  1. Props 处理
<script setup>
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  likes: Number
})

// TypeScript 版本
const props = defineProps<{
  title: string
  likes?: number
}>()
</script>
  1. 状态管理集成
// 使用 Pinia
import { useStore } from '@/stores/counter'

const store = useStore()
// 直接修改状态
store.increment()
// 使用计算属性
const double = computed(() => store.doubleCount)
  1. 异步操作处理
<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>

常见问题解决方案

  1. 响应式丢失问题
// 错误做法:解构会失去响应性
const { x, y } = reactive({ x: 1, y: 2 })

// 正确做法:使用 toRefs
const state = reactive({ x: 1, y: 2 })
const { x, y } = toRefs(state)
  1. 模板引用
<template>
  <input ref="inputRef">
</template>

<script setup>
import { ref, onMounted } from 'vue'

const inputRef = ref(null)

onMounted(() => {
  inputRef.value.focus()
})
</script>
  1. 依赖注入
// 祖先组件
import { provide } from 'vue'
provide('theme', 'dark')

// 后代组件
import { inject } from 'vue'
const theme = inject('theme', 'light') // 默认值

性能优化技巧

  1. 计算属性缓存
const expensiveValue = computed(() => {
  // 复杂计算
  return heavyCalculation(items.value)
})
  1. watch 优化
// 只监听特定属性
watch(
  () => obj.deep.nested.value,
  (newVal) => {
    console.log('变化:', newVal)
  },
  { immediate: true }
)

// 批量监听
watch([fooRef, barRef], ([foo, bar]) => {
  // 当 foo 或 bar 变化时执行
})
  1. 浅层响应式
const shallowObj = shallowReactive({
  nested: { value: 1 } // 嵌套对象不会自动转为响应式
})

与第三方库集成

  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>
  1. 动画库示例
<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>

企业级应用架构

  1. 分层架构示例
src/
├── composables/
│   ├── useApi.ts
│   ├── useFormValidation.ts
│   └── usePagination.ts
├── components/
│   ├── BasePagination.vue
│   └── BaseModal.vue
└── views/
    ├── UserList.vue
    └── UserDetail.vue
  1. 全局状态管理
// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    users: [],
    currentUser: null
  }),
  actions: {
    async fetchUsers() {
      this.users = await api.getUsers()
    }
  }
})
  1. 路由控制
<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

function navigateToUser(id) {
  router.push(`/user/${id}`)
}
</script>

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌