Vue-Vuex vs Pinia

58 阅读1分钟

✅ Vuex 模块化示例

📁 项目结构

store/
├── index.js         # 入口文件
├── modules/
│   ├── user.js
│   └── counter.js

🔸 1. store/modules/user.js

// 用户模块
export default {
  state: () => ({
    name: '张三',
    loggedIn: false,
  }),
  getters: {
    welcomeMessage(state) {
      return state.loggedIn ? `欢迎你,${state.name}` : '请先登录';
    }
  },
  mutations: {
    login(state, name) {
      state.loggedIn = true;
      state.name = name;
    },
    logout(state) {
      state.loggedIn = false;
      state.name = '';
    },
  },
  actions: {
    loginAsync({ commit }, name) {
      setTimeout(() => {
        commit('login', name);
      }, 1000);
    }
  }
};

🔸 2. store/modules/counter.js

// 计数器模块
export default {
  state: () => ({
    count: 0,
  }),
  getters: {
    double(state) {
      return state.count * 2;
    }
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  }
};

🔸 3. store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
import counter from './modules/counter';

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    user,
    counter
  }
});

组件中使用

<template>
  <div>
    <p>用户名:{{ $store.state.user.name }}</p>
    <p>登录状态:{{ $store.state.user.loggedIn ? '已登录' : '未登录' }}</p>
    <p>{{ $store.getters['user/welcomeMessage'] }}</p>

    <hr>
    <p>当前计数:{{ $store.state.counter.count }}</p>
    <p>双倍:{{ $store.getters['counter/double'] }}</p>

    <button @click="increment">+1</button>
  </div>
</template>

<script>
export default {
  methods: {
    increment() {
      this.$store.commit('counter/increment');
    }
  },
  mounted() {
    this.$store.dispatch('user/loginAsync', '李四');
    this.increment();
  }
};
</script>

<template>
  <div>
    <p>用户名:{{ name }}</p>
    <p>登录状态:{{ loggedIn ? '已登录' : '未登录' }}</p>
    <p>{{ welcomeMessage }}</p>
    <hr>
    <p>当前计数:{{ count }}</p>
    <p>双倍:{{ double }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';

export default {
  computed: {
    ...mapState('user', ['name', 'loggedIn']),
    ...mapGetters('user', ['welcomeMessage']),
    ...mapState('counter', ['count']),
    ...mapGetters('counter', ['double']),
  },
  methods: {
    ...mapMutations('counter', ['increment']),
    ...mapActions('user', ['loginAsync']),
  },
  mounted() {
    this.loginAsync('李四');
    this.increment();
  }
};
</script>


✅ Pinia 模块化示例

📁 项目结构

stores/
├── index.js              # 初始化 Pinia
├── user.js               # 用户模块
└── counter.js            # 计数器模块

🔸 1. stores/user.js

// 用户模块-选项式
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '张三',
    loggedIn: false,
  }),
  getters: {
    welcomeMessage: (state) =>
      state.loggedIn ? `欢迎你,${state.name}` : '请先登录'
  },
  actions: {
    login(name) {
      this.name = name;
      this.loggedIn = true;
    },
    logout() {
      this.name = '';
      this.loggedIn = false;
    },
    loginAsync(name) {
      setTimeout(() => {
        this.login(name);
      }, 1000);
    }
  }
});
// 用户模块-组合式
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useUserStore = defineStore('user', () => {
  const name = ref('张三');
  const loggedIn = ref(false);

  const welcomeMessage = computed(() =>
    loggedIn.value ? `欢迎你,${name.value}` : '请先登录'
  );

  function login(newName) {
    name.value = newName;
    loggedIn.value = true;
  }

  function logout() {
    name.value = '';
    loggedIn.value = false;
  }

  function loginAsync(newName) {
    setTimeout(() => {
      login(newName);
    }, 1000);
  }

  return {
    name,
    loggedIn,
    welcomeMessage,
    login,
    logout,
    loginAsync
  };
});

🔸 2. stores/counter.js

// 计数器模块-选项式
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    double: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++;
    }
  }
});
// 计数器模块-组合式
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);

  const double = computed(() => count.value * 2);

  function increment() {
    count.value++;
  }

  return {
    count,
    double,
    increment
  };
});


🔸 3. stores/index.js(创建 pinia 实例)

// 初始化 Pinia,在 main.js 中引入使用
import { createPinia } from 'pinia';
const pinia = createPinia();
export default pinia;

组件中使用

<template>
  <div>
    <p>用户名:{{ name }}</p>
    <p>登录状态:{{ loggedIn ? '已登录' : '未登录' }}</p>
    <p>{{ welcomeMessage }}</p>
    <hr>
    <p>当前计数:{{ count }}</p>
    <p>双倍:{{ double }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

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

const userStore = useUserStore();
const counterStore = useCounterStore();

// 保持响应式解构
const { name, loggedIn, welcomeMessage } = storeToRefs(userStore);
const { count, double } = storeToRefs(counterStore);

// 方法直接调用
userStore.loginAsync('李四');
counterStore.increment();
</script>

🔄 Vuex → Pinia 核心差异对照

VuexPinia
modules每个 defineStore 就是一个模块
state.moduleName.xxxstore.xxx
mutations + actions 分离actions 直接修改状态
mapState / mapGetters 解构storeToRefs() 保持响应式解构
dispatch / commit直接调用方法,如 store.loginAsync()