Vue3的组合式API把状态与逻辑从”选项块”重新组织成”功能块”:同一业务的响应式数据、计算属性与方法聚合在一起,逻辑复用不再依赖mixin,解决了命名冲突与来源不清晰两大痛点。搭配官方状态库Pinia,中大型Vue3项目能实现类型安全的全局状态与可单元测试的业务逻辑。以下以购物车与用户登录态为例,覆盖从Store定义到性能优化的完整实现。
Options API与组合式API的核心差异
Options API按”数据、方法、生命周期”分块组织代码,一个功能的逻辑散落在多个选项里;组合式API按业务功能组织,相关逻辑集中维护。同一个计数功能对比:
// Options API
export default {
data() { return { count: 0 } },
methods: { inc() { this.count++ } },
computed: { double() { return this.count * 2 } }
}
// 组合式API
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const inc = () => count.value++
const double = computed(() => count.value * 2)
return { count, inc, double }
}
}
功能简单时两种写法差别不大,组件复杂度上升后,组合式API”按功能聚合”的优势开始显现:查一个业务逻辑的所有相关代码,只需要读一段连续代码,而不是在data、methods、computed之间反复跳转。script setup语法糖进一步省去导出与return样板:
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
</script>
Pinia状态管理:Store定义与持久化插件
Pinia没有mutations、没有嵌套模块,Store直接定义并支持完整的类型推断:
// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
coupon: null
}),
getters: {
totalAmount: (state) =>
state.items.reduce((sum, i) => sum + i.price * i.qty, 0),
count: (state) => state.items.length
},
actions: {
addItem(product, qty = 1) {
const exist = this.items.find(i => i.id === product.id)
if (exist) { exist.qty += qty } else { this.items.push({ ...product, qty }) }
}
}
})
组件中使用:
<script setup>
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
const cart = useCartStore()
const { totalAmount } = storeToRefs(cart) // 解构必须走storeToRefs保持响应性
</script>
<template>
<span>{{ cart.count }}件 / {{ totalAmount.toFixed(2) }}元</span>
</template>
直接解构store实例会丢失响应性,必须通过storeToRefs包裹,这是Pinia使用中的最高频错误。登录态持久化交给插件完成:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// store内声明持久化配置,只持久化必要字段
// persist: { paths: ['coupon'] }
组合式函数:useXxx逻辑复用模式详解
跨组件复用逻辑时,把响应式状态与操作函数封装成组合式函数(Composable),取代mixin。一个带加载态的用户信息请求:
// composables/useUser.js
import { ref, onMounted } from 'vue'
export function useUser(userId) {
const user = ref(null)
const loading = ref(false)
const fetchUser = async () => {
loading.value = true
try {
const res = await fetch(`/api/users/${userId.value}`)
user.value = await res.json()
} finally {
loading.value = false
}
}
onMounted(fetchUser)
return { user, loading, refresh: fetchUser }
}
// 消费方
<script setup>
import { useUser } from '@/composables/useUser'
const { user, loading, refresh } = useUser(props.userId)
</script>
相比mixin,组合式函数的优势明确:来源清晰(import可见)、可传参、可组合、可单独做单元测试。命名约定统一为useXxx并集中放在composables目录,输入用ref或getter,返回普通对象内包含ref与函数。
组件通信方案对比与选型建议
三层结构的分工清晰:
- props/emit:父子组件单向数据流,层级不超过3层时的默认选择。
- provide/inject:跨层级配置透传与组件库内部共享,避免props逐层传递,配合readonly保证单向:
// 祖先组件
provide('theme', readonly(themeRef))
// 后代组件
const theme = inject('theme', ref('light')) // 提供默认值防止注入缺失
- Pinia:业务状态跨页面共享,登录态、购物车、字典数据等场景。把”仅UI状态”放进全局Store是常见反模式,全局状态越多,组件与Store的耦合越难拆。
性能优化:shallowRef、懒加载与大列表渲染
大列表与复杂对象场景下,浅层响应式能显著降低依赖追踪开销:
import { shallowRef, triggerRef } from 'vue'
// 万行表格数据:内部对象变更不追踪
const rows = shallowRef([])
rows.value = await fetchRows() // 整体替换触发更新
// 单行原地修改需要手动触发
rows.value[0].selected = true
triggerRef(rows)
路由级与组件级懒加载减小首包体积:
const routes = [
{ path: '/report', component: () => import('@/views/Report.vue') }
]
// 重型图表组件按需引入
const HeavyChart = defineAsyncComponent(() =>
import('@/components/HeavyChart.vue')
)
长列表渲染用虚拟滚动方案(vue-virtual-scroller)只渲染可视区域行。每轮优化完成后用Chrome Performance面板录制交互过程,对比脚本执行时间与帧率,避免没有度量依据的盲目优化。
组合式API配合Pinia与组合式函数,把Vue3项目的逻辑组织从”框架规定的结构”转变为”业务自然生长的结构”。新项目直接采用script setup加Pinia起步;存量项目迁移时优先抽取组合式函数,再逐步改造组件,避免一次性重写带来的回归风险。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-shi-zhan-pinia-zhuang-tai-guan-li-yu-zu/