← 返回题目列表

Pinia 和 Vuex 有什么区别?为什么 Vue3 更推荐 Pinia?

高频 中等 第 3 / 27 题 更新于 2026/07/29
VuePiniaVuex状态管理

简化版

Pinia 是 Vue 官方推荐的新一代状态管理库,相比 Vuex 更轻、更符合 Composition API、TypeScript 支持更好,并取消了 mutations。Vuex 仍能用,但 Vue3 新项目通常优先 Pinia。

详细版

Pinia store 示例:

export const useUserStore = defineStore('user', {
  state: () => ({ token: '', name: '' }),
  getters: {
    isLogin: (state) => Boolean(state.token),
  },
  actions: {
    setToken(token: string) {
      this.token = token;
    },
  },
});

Pinia 没有 Vuex 的 mutations,同步和异步修改都写在 actions 里。它天然支持多个 store,类型推断更舒服,也更适合 Vue3 的组合式写法。

完整版教学

一、状态管理解决跨组件共享状态

当状态只在父子组件之间传递时,propsemit 就够了。 但登录用户、权限、购物车、主题、全局字典等状态会被很多页面使用。 这时继续层层传 props 会让组件耦合越来越重。

App
  -> Layout
    -> Header
    -> Sidebar
    -> UserPanel

如果 4 个分支都要用户信息,把用户状态放到集中 store 会更清楚。 Pinia 和 Vuex 都是为这种跨组件共享状态服务的。 面试回答要先讲场景,再讲库差异。

二、Vuex 的核心模型是 state、getter、mutation、action

Vuex 经典模型比较严格。 state 存数据,getter 派生数据,mutation 同步修改状态,action 处理异步后提交 mutation。 这种分层在大型团队里有约束力,但样板代码较多。

const store = createStore({
  state: () => ({ count: 0 }),
  mutations: {
    increment(state) {
      state.count++;
    },
  },
  actions: {
    async incrementAsync({ commit }) {
      await delay(300);
      commit('increment');
    },
  },
});

一个简单 count++ 可能要经过 action、mutation、commit。 这让数据流很显式,但也增加了心智负担。 Vue2 老项目中 Vuex 很常见,维护时仍需要理解这套模型。

三、Pinia 取消 mutations,写法更直接

Pinia 保留 state、getters、actions,但取消 mutations。 同步修改和异步修改都可以写在 action 里。 这让代码更接近普通函数,也更容易做 TypeScript 推断。

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++;
    },
    async incrementAsync() {
      await delay(300);
      this.increment();
    },
  },
});

如果 count=2double 就是 4。 这种数字例子能说明 getter 是派生状态,不应该再单独存一份。 Pinia 的 action 里直接通过 this 访问状态和其他 action,写起来更短。

四、Pinia 的 TypeScript 体验更好

Pinia 的 store 是函数式使用方式,返回的 store 对象类型能被自然推断。 调用 useUserStore() 后,userStore.tokenuserStore.setToken() 都有类型提示。 这对 Vue3 + TypeScript 项目非常关键。

const userStore = useUserStore();

userStore.setToken('abc');
console.log(userStore.isLogin);
对比项VuexPinia
Vue3 推荐度可用更推荐
mutations
TypeScript相对繁琐推断更自然
模块拆分modules多个 store
Composition API可配合更贴合

这也是为什么很多 Vue3 新项目会默认选 Pinia。

五、Pinia 多 store 比 Vuex modules 更轻

Vuex 常用 modules 拆分大型状态树。 Pinia 则天然鼓励多个独立 store,例如 useUserStoreuseCartStoreusePermissionStore。 每个 store 都可以独立导入和使用。

stores/
  user.ts        -> 登录用户
  cart.ts        -> 购物车
  permission.ts  -> 权限菜单

这样拆分后,不同业务状态边界更清楚。 如果购物车页面只需要 cart,就不必关心用户模块内部结构。 这对大型后台、商城、SaaS 应用都很常见。

六、状态管理不要替代所有组件状态

不是所有状态都应该进 Pinia。 弹窗开关、输入框临时值、局部 loading,通常放在组件内部更合适。 只有跨页面共享、需要持久化、需要统一调试的状态,才适合进入 store。

适合 store:
  用户信息、权限、主题、购物车、全局配置
不适合 store:
  当前输入框内容、局部 hover、单组件弹窗开关

记忆钩子:Pinia 不是“全局变量仓库”,它是跨组件共享状态的边界工具。

如果把所有局部状态都放进 store,store 会变成杂物间。 面试官喜欢追问“哪些状态不该放”,就是看你有没有工程边界感。

七、常见误区与追问

  • 误区:用了 Pinia 就不需要 props 和 emit。 局部父子通信仍应优先用 props/emit,Pinia 适合跨层级共享状态。
  • 误区:Pinia 只是 Vuex 改名。 Pinia 取消 mutations,类型推断和多 store 模型更适合 Vue3。
  • 误区:所有接口数据都应该放进 store。 只被单页面消费的数据可以留在页面内部,避免全局状态膨胀。
  • 追问:Pinia 如何做持久化? 可以监听 store 变化写入 localStorage,或使用持久化插件,但要注意敏感信息安全。
  • 追问:Pinia 里 action 可以异步吗? 可以,Pinia 的 actions 同时支持同步和异步逻辑。
  • 追问:Vuex 老项目要不要立刻迁移? 不必为了迁移而迁移;新模块可逐步引入 Pinia,旧 Vuex 按业务节奏替换。

八、加强记忆

这题按“状态管理场景、Vuex 模型、Pinia 改进、边界选择”来答。先说跨组件共享状态才需要 store,再说 Vuex 有 mutation/action 分层,Pinia 取消 mutations、类型推断更好;最后补一句不是所有状态都进 store,局部状态留在组件内。