Skip to content
第 13 章 前端 ⏱ 13 分钟阅读

第 13 章:Composition API 实战 ​

学习目标 ​

  • 对比 Options API 与 Composition API
  • 用 ref / computed / watch 组织业务
  • 抽取逻辑到组合式函数
  • 选出当前场景的最优写法

一、Options API 写法回顾 ​

vue
<script lang="ts">
export default {
  data() {
    return { count: 0, keyword: '' }
  },
  computed: {
    filtered() { return this.list.filter(i => i.name.includes(this.keyword)) },
  },
  watch: {
    keyword(v) { this.fetch(v) },
  },
  methods: {
    inc() { this.count++ },
    async fetch(k: string) { /* ... */ },
  },
  mounted() { this.fetch('') },
}
</script>

二、Composition API 等价改写 ​

vue
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'

const count = ref(0)
const keyword = ref('')
const list = ref<Item[]>([])

const filtered = computed(() =>
  list.value.filter(i => i.name.includes(keyword.value))
)

watch(keyword, (v) => fetchList(v))

const inc = () => count.value++

async function fetchList(k: string) {
  const res = await api.list({ keyword: k })
  list.value = res.data
}

onMounted(() => fetchList(''))
</script>

代码按业务逻辑组织,不再被 data/computed/watch 隔开。

三、优缺点对比 ​

维度OptionsComposition
学习曲线友好需熟悉 hooks
复用mixin(有命名冲突)composables
大组件难维护抽取清晰
TS 推断一般强

四、实战:搜索 + 分页 ​

ts
// src/composables/useSearch.ts
import { ref, computed, watch } from 'vue'

export function useSearch<T>(
  fetcher: (params: { keyword: string; page: number }) => Promise<{ items: T[]; total: number }>,
) {
  const keyword = ref('')
  const page = ref(1)
  const pageSize = 20
  const list = ref<T[]>([])
  const total = ref(0)
  const loading = ref(false)

  const totalPages = computed(() => Math.ceil(total.value / pageSize))

  async function load() {
    loading.value = true
    try {
      const res = await fetcher({ keyword: keyword.value, page: page.value })
      list.value = res.items
      total.value = res.total
    } finally {
      loading.value = false
    }
  }

  watch(keyword, () => { page.value = 1; load() })
  watch(page, load)

  return { keyword, page, list, total, totalPages, loading, load }
}
vue
<script setup lang="ts">
import { useSearch } from '@/composables/useSearch'
import { fetchUsers } from '@/api/user'

const { keyword, page, list, totalPages, loading } = useSearch(fetchUsers)
</script>

⚠️ 坑 1:组合式函数必须以 use 开头命名,否则 lint 报错。 ⚠️ 坑 2:组合式函数里不要直接调用生命周期钩子以外的副作用,惰性用 watch 包裹。

五、provide / inject 配合组合式 ​

ts
// src/composables/useTheme.ts
import { provide, inject, ref, type InjectionKey } from 'vue'

const ThemeKey: InjectionKey<Ref<string>> = Symbol('theme')

export function provideTheme() {
  const theme = ref('light')
  provide(ThemeKey, theme)
  return theme
}

export function useTheme() {
  return inject(ThemeKey, ref('light'))
}
vue
<!-- App.vue -->
<script setup lang="ts">
const theme = provideTheme()
</script>

<!-- 后代组件 -->
<script setup lang="ts">
const theme = useTheme()
</script>

⚠️ 坑 3:InjectionKey 不写的话,inject 拿到的是 unknown 类型。

六、本章小结 ​

场景写法
简单组件Options 或 Setup 皆可
复杂业务Setup + 组合式函数
跨组件上下文provide / inject
类型安全InjectionKey<T>

动手练习 ​

  1. 重构:把 Options API 写的搜索页改成 Composition API
  2. 抽取:把分页逻辑抽成 usePagination
  3. 主题切换:用 composable 包装 theme,所有组件可读

下一章:第 14 章:组合式函数 →

本站基于 VitePress 构建 · 由 StackHub 团队维护