美文网首页产品
Vuex 模块的基础使用和命名空间的使用

Vuex 模块的基础使用和命名空间的使用

作者: 扶得一人醉如苏沐晨 | 来源:发表于2023-02-24 16:26 被阅读0次

    我们在使用Vue开发项目时,如果项目比较复杂的话,使用Vuex进行状态管理会大大简化开发难度,提高开发效率。
    但是当使用Vuex的一些复杂功能(如:模块、命名空间)时,Vuex会变的稍微有些复杂。再加上Vuex的灵活性,同样的功能可以有很多种实现方式。
    因为太灵活、实现方式太多,导致经常会记混掉
    所以就整理了一下Vuex的几种常用的使用方式以实例作个对比

    一、基础概念

    1.1、Vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。

    1.2、vuex中的模块(module)

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

    1.3、模块的命名空间

    默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

    二、vuex的常规用法(不使用模块(module)的store)

    2.1、创建一个简单的store

    store/index.js的内容

    import Vue from 'vue'
    import Vuex from 'vuex'
    import api from '@/api/index'
    
    Vue.use(Vuex)
    const store = new Vuex.Store({
      state: {
        loading : false, // 是否加载中(接口请求时的全屏loading效果)
        userInfo: {} // 用户信息
      },
      getters: {
        loading: state => state.loading,
        userInfo: state => state.userInfo, 
        userName: state => state.userInfo.userName || ''
      },
      mutations: {
        setLoading(state, payload) {
          state.loading = payload
        },
        setUserInfo(state, payload) {
          state.userInfo = payload
        }
      },
      actions: {
        // 获取用户信息
        queryUserInfoAction({ commit }) {
          api.queryUserInfo().then((res) => {
            let result = res && res.result || {}
            commit('setUserInfo', result)
          })
        }
      }
    })
    
    export default store
    

    2.2、在组件中使用

    import { mapGetters, mapActions } from 'vuex'
    export default {
      computed: {
        // 【传统方式】获取store中的数据
        /*
        loading() {
          return this.$store.getters.loading
        },
        userName() {
          return this.$store.getters.userName
        },
        */
        // 【辅助函数方式】获取store中的数据(代码更简洁)
        ...mapGetters(['loading', 'userName']),
      },
      created() {
        // 如果没有用户名,则查询用户信息,已有则不需查询(减少不必要的http请求)
        
        // 【传统方式】请求异步数据
        !this.userName && this.$store.dispatch('queryUserInfoAction')
        
        // 【辅助函数方式】请求异步数据
        !this.userName && this.queryUserInfoAction()
      },
      methods: {
        ...mapActions(['queryUserInfoAction']), // 查询用户信息的aciton
        // 打开loading效果
        openLoading() {
            this.$store.commit('setLoading', true)
        },
        // 关闭loading效果
        closeLoading() {
            this.$store.commit('setLoading', false)
        }
      }
    }
    
    

    三、vuex中模块的基础使用(不开启命名空间时)

    默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的
    这样使得多个模块能够对同一 mutation 或 action 作出响应。
    

    对于action、mutation 和 getter,是在模块中还是在全局中,它们的使用方式是相同的,只是state会有所不同,模块中的state会多一层模块名。格式变成store.state.模块名.状态名(根state中的格式为store.state.状态名)。

    3.1、目录结构

    ├── api             # api请求目录
    │   ├── index.js        # 所有api请求
    ├── store           # store目录
    │   ├── modules     # store中的所有模块
    │   │   ├── theme.js    # 主题模块
    │   │   ├── product.js  # 产品模块
    │   ├── index.js        # store主文件
    
    

    3.2、创建一个带模块的store

    store主文件(store/index.js)内容

    import Vue from 'vue'
    import Vuex from 'vuex'
    import api from '@/api/index'
    import themeModule from './modules/theme.js' // 主题模块
    import productModule from './modules/product.js' // 产品模块
    Vue.use(Vuex)
    const store = new Vuex.Store({
      state: {
        loading : false, // 是否加载中(接口请求时的全屏loading效果)
        userInfo: {} // 用户信息
      },
      getters: {
        loading: state => state.loading,
        userInfo: state => state.userInfo, 
        userName: state => state.userInfo.userName || ''
      },
      mutations: {
        setLoading(state, payload) {
          state.loading = payload
        },
        setUserInfo(state, payload) {
          state.userInfo = payload
        }
      },
      actions: {
        // 获取用户信息
        queryUserInfoAction({ commit }) {
          api.queryUserInfo().then((res) => {
            let result = res && res.result || {}
            commit('setUserInfo', result)
          })
        }
      },
      modules: {
        themeModule, // 主题模块
        productModule, // 产品数据
      }
    })
    export default store
    

    主题模块(store/modules/theme.js)内容

    import api from '@/api/index'
    
    const themeModule = {
      state: {
        themeObj: {} // 主题数据
      },
      getters: {
        themeData: state => {
          return {
            primaryColor: '#fe3132', // 主颜色(如:按钮背景颜色)
            subBgColor: '#fff6f6', // 次要颜色(如:浅色背景色)
            ...state.themeObj // 没有返回数据使用以上默认值,有则覆盖以上数据
          }
        }
      },
      mutations: {
        setTheme(state, payload) {
          state.themeObj = payload
        }
      },
      actions: {
        queryThemeAction({ commit }) {
          return api.queryTheme().then(res => {
            let data = (res && res.result) || {}
            commit('setTheme',  data)
          })
        }
      }
    }
    export default themeModule
    

    产品模块(store/modules/product.js)内容

    import api from '@/api/index'
    const productModule = {
      state: {
        proData: {}, // 产品数据
        indexDataRes: {} // 产品首页数据
      },
      getters: {
        proName: state => state.proData.proName || '', // 产品名称
        proDesc: state => state.proData.proDesc || '' // 产品描述
        indexData: state => state.indexDataRes // 产品首页数据
      },
      mutations: {
        // 设置产品数据
        setProData(state, payload) {
          state.proData = payload
        },
        // 设置产品首页数据
        setIndexData(state, payload) {
          state.indexDataRes = payload
        },
      },
      actions: {
        // 获取产品数据
        queryProDataAction(context) {
          return api.queryProData().then(res => {
            // 页面数据
            let data = (res && res.result) || {}
            context.commit('setProData',  data)
          })
        },
        // 获取首页数据
        queryIndexDataAction(context) {
          return api.queryIndexData().then(res => {
            let data = (res && res.result) || {}
            context.commit('setIndexData',  data)
          })
        }
      }
    }
    export default productModule
    

    3.3、在组件中使用

    import { mapState, mapGetters, mapActions } from 'vuex'
    
    export default {
      
      computed: {
        // 【传统方式】获取store中的数据
        // 注意state和getter数据结构的区别(state需要带上模块名,而getter不需要模块名)
        /*
        proData() {
            return this.$store.state.productModule.proData
        },
        themeData() {
          return this.$store.getters['themeData']
        },
        proName() {
          return this.$store.getters['proName']
        },
        proDesc() {
          return this.$store.getters['proDesc']
        },
        indexData() {
          return this.$store.getters['indexData']
        },
        */
        
        // 【辅助函数方式】获取store中的数据(代码更简洁)
        ...mapState({ proData: state => state.productModule.proData }),
        ...mapGetters(['themeData']),
        ...mapGetters(['proName', 'proDesc', 'indexData'])
      },
      created() {
        // 【传统方式】获取异步数据
        // this.$store.dispatch('queryThemeAction') // 获取主题数据
        // this.$store.dispatch('queryProDataAction') // 获取产品数据
        // this.$store.dispatch('queryIndexDataAction') // 获取首页数据
        
        // 【辅助函数方式】获取异步数据(需要先在methods中使用mapActions定义方法)
        this.queryThemeAction() // 获取主题数据
        this.queryProDataAction() // 获取产品数据
        this.queryIndexDataAction() // 获取首页数据
      },
      methods: {
        ...mapActions(['queryThemeAction']),
        ...mapActions(['queryProDataAction', 'queryIndexDataAction'])
      }
    }
    

    四、vuex中【开启命名空间】的模块

    如果你想模块之间相互独立、互不影响。可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 和 mutation 都会自动根据模块注册的路径调整命名。所以开启命名空间的模块中的getter、action 和 mutation的使用方式都会改变。
    但是开启命名空间和不开启命名空间的模块中的state的使用方式不会改变。格式依然是store.state.模块名.状态名。

    4.1、开启模块的命名空间

    const moduleName = {
      state: {
        // 此处代码省略...
      },
      getters: {
        // 此处代码省略...
      },
      mutations: {
        // 此处代码省略...
      },
      actions: {
        // 此处代码省略...
      },
      namespaced: true // 开启命名空间
    }
    export default moduleName
    

    4.2、在组件中使用

    import { mapState, mapGetters, mapActions } from 'vuex'
    export default {
      computed: {
        // 【传统方式】获取store中的数据
        /*
        proData() {
            return this.$store.state.productModule.proData
        },
        themeData() {
          return this.$store.getters['themeModule/themeData']
        },
        proName() {
          return this.$store.getters['productModule/proName']
        },
        proDesc() {
          return this.$store.getters['productModule/proDesc']
        },
        indexData() {
          return this.$store.getters['productModule/indexData']
        },
        */
        
        // 【辅助函数方式一】获取store中的数据(代码更简洁)
        /*
        ...mapState({ proData: state => state.productModule.proData }),
        ...mapGetters(['themeModule/themeData']),
        ...mapGetters(['themeModule/proName', 'themeModule/proDesc', 'themeModule/indexData'])
        */
        
        // 【辅助函数方式二】获取store中的数据(代码最简洁)
        // 将模块的空间名称字符串作为第一个参数传递给辅助函数,这样所有绑定都会自动将该模块作为上下文。
        ...mapState('productModule', { proData: state => state.proData }),
        ...mapGetters('themeModule', ['themeData']),
        ...mapGetters('productModule', ['proName', 'proDesc', 'indexData'])
      },
      created() {
        // 【传统方式】获取异步数据
        // this.$store.dispatch('themeModule/queryThemeAction') // 获取主题数据
        // this.$store.dispatch('productModule/queryProDataAction') // 获取产品数据
        // this.$store.dispatch('productModule/queryIndexDataAction') // 获取首页数据
        
        // 【辅助函数方式】获取异步数据(需要先在methods中使用mapActions定义方法)
        this.queryThemeAction() // 获取主题数据
        this.queryProDataAction() // 获取产品数据
        this.queryIndexDataAction() // 获取首页数据
      },
      methods: {
        ...mapActions('themeModule', ['queryThemeAction']),
        ...mapActions('productModule', ['queryProDataAction', queryIndexDataAction])
      }
    }
    
    

    五、扩展

    模块:

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能会变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

    命名空间:

    默认情况下,模块内部的 action、mutation、getter是注册在全局命名空间的 — 这样使得多个模块能够对同一 mutation 或 action 做出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced:true 的方式使其成为带命名的模块。当模块被注册后,他所有 getter、action、及 mutation 都会自动根据模块注册的路径调整命名。

    相关文章

      网友评论

        本文标题:Vuex 模块的基础使用和命名空间的使用

        本文链接:https://www.haomeiwen.com/subject/rbexldtx.html