Skip to content

Pinia 状态管理

概述

Pinia 是 Vue 官方推荐的状态管理库,替代 Vuex。

一、Pinia vs Vuex

特性PiniaVuex 4
mutations必需
模块扁平化嵌套
TypeScript原生支持需配置
Composition API有限
DevTools
包大小~1KB~10KB

二、定义 Store

Options Store

javascript
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Counter'
  }),
  
  getters: {
    double: (state) => state.count * 2,
    
    // 使用其他 getter
    doublePlusOne() {
      return this.double + 1;
    }
  },
  
  actions: {
    increment() {
      this.count++;
    },
    
    async fetchData() {
      const res = await fetch('/api/data');
      this.count = await res.json();
    }
  }
});

Setup Store

javascript
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  // state
  const count = ref(0);
  
  // getters
  const double = computed(() => count.value * 2);
  
  // actions
  function increment() {
    count.value++;
  }
  
  return { count, double, increment };
});

三、使用 Store

vue
<script setup>
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';

const store = useCounterStore();

// 直接使用
console.log(store.count);
store.increment();

// 解构(失去响应式)
const { count } = store; // ❌

// storeToRefs 保持响应式
const { count, double } = storeToRefs(store); // ✅

// actions 可以直接解构
const { increment } = store; // ✅
</script>

<template>
  <p>{{ count }}</p>
  <button @click="increment">+1</button>
</template>

四、修改 State

javascript
const store = useCounterStore();

// 直接修改
store.count++;

// $patch 批量修改
store.$patch({
  count: store.count + 1,
  name: 'New Name'
});

// $patch 函数形式
store.$patch((state) => {
  state.count++;
  state.items.push({ id: 1 });
});

// 替换整个 state
store.$state = { count: 0, name: 'Reset' };

// 重置
store.$reset();

五、插件

javascript
import { createPinia } from 'pinia';

const pinia = createPinia();

// 持久化插件
pinia.use(({ store }) => {
  // 从 localStorage 恢复
  const saved = localStorage.getItem(store.$id);
  if (saved) {
    store.$patch(JSON.parse(saved));
  }
  
  // 订阅变化
  store.$subscribe((mutation, state) => {
    localStorage.setItem(store.$id, JSON.stringify(state));
  });
});

app.use(pinia);

六、Store 间通信

javascript
import { useUserStore } from './user';
import { useCartStore } from './cart';

export const useOrderStore = defineStore('order', () => {
  const userStore = useUserStore();
  const cartStore = useCartStore();
  
  async function createOrder() {
    return await api.createOrder({
      userId: userStore.id,
      items: cartStore.items
    });
  }
  
  return { createOrder };
});

面试高频题

Q1: Pinia 为什么不需要 mutations?

Actions 可以直接修改 state,同步异步统一处理,代码更简洁。DevTools 仍能追踪状态变化。

Q2: 如何实现持久化?

使用 $subscribe 监听变化,存储到 localStorage,初始化时恢复。

Q3: storeToRefs 的作用?

解构 store 时保持响应式,因为 store 是 reactive 对象,直接解构会失去响应式。

前端面试知识库