Vuex 进阶,模块化开发(Modules):解决单状态树臃肿问题

当 Vue 项目业务越来越复杂时,全局store会变得庞大且难以维护 ------Vuex 的Modules(模块化) 功能可以将store按业务拆分为多个子模块,每个模块独立管理自己的statemutationsactionsgetters,大幅提升代码的可维护性。

一、为什么要用 Vuex Modules?

Vuex 是单状态树(所有数据存在一个state中),随着业务增长:

  • store会变得越来越大,代码耦合度高;
  • 不同业务的mutations/actions容易重名;
  • 团队协作时难以拆分工作。

而 Modules 可以将store按业务(如 "用户模块""购物车模块")拆分,每个模块拥有独立的状态管理逻辑。

二、Vuex Modules 的基础使用

1. 步骤 1:创建子模块文件

src/store/modules目录下创建子模块(以 "用户模块" 为例):

javascript 复制代码
// src/store/modules/user.js
export default {
  // 开启命名空间(关键!实现模块隔离)
  namespaced: true,
  
  // 模块内的state
  state: {
    userInfo: null, // 用户信息
    token: ''       // 登录凭证
  },
  
  // 模块内的mutations
  mutations: {
    SET_USER_INFO(state, userInfo) {
      state.userInfo = userInfo;
    },
    SET_TOKEN(state, token) {
      state.token = token;
    }
  },
  
  // 模块内的actions
  actions: {
    async login({ commit }, form) {
      // 模拟接口请求
      const res = await new Promise(resolve => {
        setTimeout(() => {
          resolve({ 
            userInfo: { name: '张三', age: 20 }, 
            token: 'fake_token_123' 
          });
        }, 1000);
      });
      commit('SET_USER_INFO', res.userInfo);
      commit('SET_TOKEN', res.token);
      return res;
    }
  },
  
  // 模块内的getters
  getters: {
    isLogin(state) {
      return !!state.token; // 判断是否登录
    }
  }
};

2. 步骤 2:在 store 中注册子模块

src/store/index.js中引入并注册子模块:

javascript 复制代码
// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user'; // 引入用户模块

Vue.use(Vuex);

export default new Vuex.Store({
  // 注册子模块(key为模块名,value为模块对象)
  modules: {
    user
  }
});

三、开启命名空间(namespaced: true

默认情况下,子模块的mutations/actions/getters会被挂载到全局命名空间,无法实现模块隔离。

必须在模块中添加namespaced: true,才能让模块拥有独立的命名空间,避免不同模块的方法重名。

四、模块内核心成员的使用方式

开启命名空间后,模块内的state/mutations/actions/getters需要通过模块名访问。

1. 访问模块内的 state

方式 1:直接通过$store.state.模块名访问(不推荐)

vue 复制代码
<template>
  <!-- 直接访问:$store.state.模块名.数据 -->
  <div>用户名:{{ $store.state.user.userInfo?.name }}</div>
</template>

方式 2:通过mapState辅助函数(推荐)

vue 复制代码
<script>
import { mapState } from 'vuex';
export default {
  computed: {
    // 格式:...mapState('模块名', ['要映射的state数据'])
    ...mapState('user', ['userInfo', 'token'])
  }
};
</script>

<template>
  <div>用户名:{{ userInfo?.name }}</div>
  <div>Token:{{ token }}</div>
</template>

userInfo 初始值是 null(或接口请求未返回时也是 undefined),如果直接写 userInfo.name,会触发 JavaScript 报错:

plaintext 复制代码
Cannot read properties of null (reading 'name')

这个报错会导致页面渲染中断,而 ?. 可以安全访问嵌套属性:

  • userInfonull/undefined 时,userInfo?.name 会直接返回 undefined,不会报错;
  • userInfo 有值时,正常读取 name 属性。

2. 调用模块内的 mutations

方式 1:直接通过$store.commit('模块名/函数名')调用

vue 复制代码
<script>
export default {
  methods: {
    logout() {
      // 格式:$store.commit('模块名/mutations函数名', 参数)
      this.$store.commit('user/SET_TOKEN', '');
    }
  }
};
</script>

方式 2:通过mapMutations辅助函数(推荐)

vue 复制代码
<script>
import { mapMutations } from 'vuex';
export default {
  methods: {
    // 格式:...mapMutations('模块名', ['要映射的mutations函数'])
    ...mapMutations('user', ['SET_TOKEN']),
    logout() {
      this.SET_TOKEN(''); // 直接调用映射后的方法
    }
  }
};
</script>

3. 调用模块内的 actions

方式 1:直接通过$store.dispatch('模块名/函数名')调用

vue 复制代码
<script>
export default {
  async onLogin() {
    const form = { username: 'zhangsan', password: '123456' };
    // 格式:$store.dispatch('模块名/actions函数名', 参数)
    const res = await this.$store.dispatch('user/login', form);
    console.log('登录结果:', res);
  }
};
</script>

方式 2:通过mapActions辅助函数(推荐)

vue 复制代码
<script>
import { mapActions } from 'vuex';
export default {
  methods: {
    // 格式:...mapActions('模块名', ['要映射的actions函数'])
    ...mapActions('user', ['login']),
    async onLogin() {
      const form = { username: 'zhangsan', password: '123456' };
      const res = await this.login(form); // 直接调用映射后的方法
      console.log('登录结果:', res);
    }
  }
};
</script>

4. 访问模块内的 getters

方式 1:直接通过$store.getters['模块名/getter名']访问(不推荐)

vue 复制代码
<template>
  <!-- 直接访问:$store.getters['模块名/getter名'] -->
  <div>是否登录:{{ $store.getters['user/isLogin'] ? '是' : '否' }}</div>
</template>

方式 2:通过mapGetters辅助函数(推荐)

vue 复制代码
<script>
import { mapGetters } from 'vuex';
export default {
  computed: {
    // 格式:...mapGetters('模块名', ['要映射的getters'])
    ...mapGetters('user', ['isLogin'])
  }
};
</script>

<template>
  <div>是否登录:{{ isLogin ? '是' : '否' }}</div>
</template>

五、Vuex Modules 的优势总结

  • 拆分代码 :按业务拆分store,避免单状态树臃肿;
  • 隔离命名 :开启namespaced后,不同模块的方法不会重名;
  • 团队协作:不同开发者可以维护不同模块,降低冲突风险。
相关推荐
Aftery的博客2 小时前
Uniapp-vue实现语言功能切换(多语言)
javascript·vue.js·uni-app
喵了几个咪2 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:用 JavaScript/Lua 解锁动态业务扩展能力
javascript·后端·微服务·golang·lua·admin
旧梦吟2 小时前
脚本网页 linux内核源码讲解
linux·前端·stm32·算法·html5
李少兄3 小时前
深入理解 CSS :not() 否定伪类选择器
前端·css
程序员码歌9 小时前
短思考第261天,浪费时间的十个低效行为,看看你中了几个?
前端·ai编程
Swift社区10 小时前
React Navigation 生命周期完整心智模型
前端·react.js·前端框架
若梦plus10 小时前
从微信公众号&小程序的SDK剖析JSBridge
前端
用泥种荷花11 小时前
Python环境安装
前端