Vue3组合式API实战:Pinia状态管理与组件逻辑复用模式

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/

(0)
小编小编
上一篇 2小时前
下一篇 2小时前

相关推荐