从 Options API 到 Composition API
Vue3 带来的最大变化是组合式 API(Composition API)。它不取代 Options API,而是让「按逻辑关注点」组织代码成为可能——一个功能的 state、computed、方法可以写在一起,而不是被迫拆散在 data、methods、computed 各个区块。
一、setup 与 ref / reactive
<script setup>
import { ref, reactive, computed } from 'vue'
// 基本类型用 ref
const count = ref(0)
function inc() { count.value++ }
// 对象用 reactive
const user = reactive({ name: '张三', age: 28 })
// 或也用 ref(推荐:统一心智模型)
const user2 = ref({ name: '张三', age: 28 })
console.log(user2.value.name)
// 计算属性
const double = computed(() => count.value * 2)
</script>
经验法则:基本类型用 ref,对象/数组也用 ref,保持统一;reactive 仅在明确需要时使用,且注意它不能整体替换(会丢失响应性)。
二、逻辑复用:组合式函数(Composables)
组合式 API 的精髓是「自定义 Hook」——把可复用逻辑抽成 useXxx 函数。
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const double = computed(() => count.value * 2)
const inc = () => count.value++
return { count, double, inc }
}
// 组件里直接用
import { useCounter } from './useCounter'
const { count, double, inc } = useCounter(10)
比起 Vue2 的 mixin,组合式函数没有命名冲突、来源清晰、类型友好。
三、状态管理:为什么选 Pinia
Pinia 是 Vue 官方推荐的状态库,取代了 Vuex。它更轻、API 更简洁、对 TypeScript 友好,且没有 Vuex 里令人头疼的 mutations。
定义一个 store
// stores/cart.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCartStore = defineStore('cart', () => {
// state
const items = ref([])
// getters
const total = computed(() =>
items.value.reduce((s, i) => s + i.price * i.qty, 0)
)
const count = computed(() => items.value.length)
// actions
function add(product, qty = 1) {
const line = items.value.find(i => i.id === product.id)
if (line) line.qty += qty
else items.value.push({ ...product, qty })
}
function remove(id) {
items.value = items.value.filter(i => i.id !== id)
}
return { items, total, count, add, remove }
})
在组件中使用
<script setup>
import { useCartStore } from '@/stores/cart'
const cart = useCartStore()
cart.add({ id: 1, name: '键盘', price: 299 })
console.log(cart.total, cart.count)
</script>
<!-- 模板里直接用 -->
<p>共 {{ cart.count }} 件,合计 {{ cart.total }} 元</p>
四、Pinia 进阶要点
- 状态持久化:用
pinia-plugin-persistedstate把购物车存到 localStorage,刷新不丢。 - 跨 store 调用:在某个 store 的 action 里直接
useOtherStore()即可,无需层层传参。 - devtools:Pinia 原生支持 Vue DevTools 时间旅行调试,定位状态变更极方便。
- 与 Setup Store 取舍:函数式(本文写法)更灵活;如果要严格类型推导也可用
defineStore('id', { state, getters, actions })选项式。
五、常见坑
- 解构丢失响应性:
const { count } = useCartStore()会失去响应性,要用storeToRefs(store)。 - 在外部(非组件)使用 store:必须保证 Pinia 已
app.use(pinia)安装后再调用。 - 避免在 state 里放非响应式大对象:如整个 DOM 节点。
小结
Vue3 的组合式 API 解决了「逻辑按功能聚合」的问题,Pinia 用极简的 API 接管了全局状态。两者配合,一个购物车、用户态、主题切换这类场景都能写得清晰可维护。记住 storeToRefs 解构和持久化插件两个关键点,基本就能避开九成坑。




