Skip to content

vue 状态管理

在 vue2,vuex 是管理状态的主流,到了 vue3 则 pinia 成了主流! 这是因为 Pinia 是基于 Vue 3 的 Composition API 设计的,这使得它更加灵活和可组合。
而 Vuex 则是基于 Vue 2 的 Options API 构建的。

PiniaVsVuex3.x

初始化

Vuex需要显式安装插件并创建store,而Pinia则更直观,无需安装插件,而是创建一个实例直接注册到Vue应用中。

js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex); // 安装插件

new Vue({
  store: new Vuex.Store({}), // 挂在到vue实例上
  render: (h) => h(App),
}).$mount("#app");
js
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";

const pinia = createPinia();
const app = createApp(App);

app.use(pinia); // 注册到Vue应用
app.mount("#app");

使用

在组件内部,Vuex通过this.$store访问状态和触发变更,而Pinia则使用Composition API风格,通过setup函数内的useStore来访问。

html
<template>
    <div>
       <div> {{count}} </div>
       <button @click="increment()">测试</button>
    </div>
<template>
<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    }
  },
  methods: {
    increment() {
      this.$store.commit('increment');
    }
  }
}
</script>
html
<template>
    <div>
       <div> {{count}} </div>
       <button @click="increment()">测试</button>
    </div>
<template>
<script setup>
import { useStore } from './store'
const count = store.count;
const increment = store.increment;
</script>

定义

Vuex将状态、actions和getters分开定义,而Pinia则在一个对象中统一定义,简化了代码结构。
Pinia 相较于 Vuex 更加简洁、直接,特别是去掉了 mutations 的概念(action可以直接修改 state,不需要像 Vuex 那样通过 mutation。),简化了代码结构。

js
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment({ commit }) {
      commit('increment');
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
});
js
import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++;
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  }
});

模块化支持

Vuex需要手动配置模块到modules里,而Pinia的每个store都可以视为一个模块,支持更灵活的模块化。

js
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const store = new Vuex.Store({
  modules: {
    a: moduleA
  }
});
js
// 使用 defineStore 分别定义不同的模块并在组件中自由使用。
export const useModuleA = defineStore('moduleA', {
  // ...
});

ts支持

Pinia从一开始就设计成支持TypeScript,而Vuex的TypeScript支持稍显不足,需要额外的类型声明等配置。

在这个例子中,count的状态被明确地标记为number类型。当你在组件中使用这个store时,TypeScript可以提供类型检查和自动完成功能。

js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0 as number
  }),
  actions: {
    increment() {
      this.count++;
    }
  }
});

Vuex中,如果没有额外的配置,TypeScript可能无法识别状态的类型。你需要使用Module类型来提供类型信息

js
import { Store, Module } from 'vuex';

interface State {
  count: number;
}

const store: Store<State> = new Vuex.Store<State>({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  }
});