Vuex分模块使用

发布于 2025-02-09  713 次阅读


在 Vue 2 中使用 Vuex 进行状态管理时,如果你的应用变得复杂,可以将 Vuex store 分割成多个模块(module)。每个模块拥有自己的 state、mutations、actions、getters 等,使得状态管理更加清晰和模块化。

以下是一个基于 Vue 2 和 Vuex 的模块化示例:

1. 创建 Vuex Store 并定义模块

首先,在 src/store 目录下创建一个 index.js 文件,并在其中定义 Vuex store,同时引入你将要创建的模块。

// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import authModule from './modules/auth'; // 引入认证模块
import userModule from './modules/user'; // 引入用户模块

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    auth: authModule, // 注册认证模块
    user: userModule  // 注册用户模块
  }
});

接着,在 src/store/modules 目录下创建 auth.jsuser.js 文件,分别定义认证模块和用户模块。

认证模块(auth.js)

// src/store/modules/auth.js
const authModule = {
  namespaced: true, // 启用命名空间
  state: {
    isLoggedIn: false,
    user: null
  },
  mutations: {
    setUser(state, user) {
      state.user = user;
      state.isLoggedIn = !!user;
    },
    logout(state) {
      state.user = null;
      state.isLoggedIn = false;
    }
  },
  actions: {
    login({ commit }, user) {
      // 模拟登录逻辑
      setTimeout(() => {
        commit('setUser', user);
      }, 1000);
    },
    logout({ commit }) {
      commit('logout');
    }
  },
  getters: {
    isLoggedIn: state => state.isLoggedIn,
    currentUser: state => state.user
  }
};

export default authModule;

用户模块(user.js)

// src/store/modules/user.js
import profileModule from './profile'; // 引入用户资料子模块

const userModule = {
  namespaced: true, // 启用命名空间
  modules: {
    profile: profileModule // 注册用户资料子模块
  },
  state: {
    username: ''
  },
  mutations: {
    setUsername(state, username) {
      state.username = username;
    }
  },
  actions: {
    updateUsername({ commit }, username) {
      commit('setUsername', username);
    }
  },
  getters: {
    username: state => state.username
  }
};

export default userModule;

同时,你可以在 src/store/modules 目录下创建 profile.js 文件,定义用户资料子模块。

// src/store/modules/profile.js
const profileModule = {
  state: {
    bio: ''
  },
  mutations: {
    setBio(state, bio) {
      state.bio = bio;
    }
  },
  actions: {
    updateBio({ commit }, bio) {
      commit('setBio', bio);
    }
  },
  getters: {
    bio: state => state.bio
  }
};

export default profileModule;

2. 在主应用中引入 Store

这一步与前面的非模块化示例相同,只需确保在 main.js 中引入了 store/index.js 定义的 store。

// src/main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store';

new Vue({
  store,
  render: h => h(App)
}).$mount('#app');

3. 在组件中使用模块化 Store

由于启用了命名空间,你需要在组件中通过指定模块名来访问状态、提交 mutation 或分发 action。

<template>
  <div>
    <p>Username: {{ username }}</p>
    <p>Bio: {{ bio }}</p>
    <p v-if="isLoggedIn">Logged in as: {{ currentUser.username }}</p>
    <button @click="updateUsername('newUsername')">Update Username</button>
    <button @click="updateBio('New bio description')">Update Bio</button>
    <button @click="login">Login</button>
    <button @click="logout" v-if="isLoggedIn">Logout</button>
  </div>
</template>

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

export default {
  computed: {
    ...mapState({
      // 从根 state 映射状态(如果有的话)
    }),
    ...mapState('user', { // 从 user 模块映射状态
      username: state => state.username
    }),
    ...mapGetters('user/profile', { // 从 user/profile 模块映射 getter
      bio: 'bio'
    }),
    ...mapGetters('auth', { // 从 auth 模块映射 getter
      isLoggedIn: 'isLoggedIn',
      currentUser: 'currentUser'
    })
  },
  methods: {
    ...mapActions('user', ['updateUsername']), // 从 user 模块映射 action
    ...mapActions('user/profile', ['updateBio']), // 从 user/profile 模块映射 action
    ...mapActions('auth', ['login', 'logout']) // 从 auth 模块映射 action
    // 你也可以直接调用 action,但需要使用完整的命名空间路径
    // login() {
    //   this.$store.dispatch('auth/login', { username: 'admin', password: 'password' });
    // }
  }
};
</script>

在这个组件中,我们使用了 mapStatemapGettersmapActions 辅助函数来映射用户模块和用户资料子模块的状态、getter 和 action,以及认证模块的状态和 action。注意,在映射时,我们需要指定模块名(如 useruser/profileauth)来访问相应的状态、getter 和 action。

通过这种方式,你可以将 Vuex store 分割成多个模块,使得状态管理更加清晰和模块化。每个模块都可以有自己的状态、mutations、actions 和 getters,同时可以通过命名空间来避免命名冲突。

下次见面会是什么时候呢?
最后更新于 2025-02-09