【Vue】Vuex 模块的基础使用--命名空间

我们在使用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.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 页面刷新数据丢失怎么解决?
需要做 vuex 数据持久化,一般使用本地储存的方案来保存数据,可以自己设计存储方案,也可以使用第三方插件。
推荐使用 vuex-persist (脯肉赛斯特)插件,它是为 Vuex 持久化储存而生的一个插件。不需要你手动存取 storage,而是直接将状态保存至 cookie 或者 localStorage中。

  • 12
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Vue 3中使用Vuex,你需要进行一些配置和使用方法上的改变。下面是使用Vuex的一般步骤: 1. 安装Vuex:在项目根目录下运行以下命令安装Vuex: ``` npm install vuex ``` 2. 创建store实例:在项目的src目录下创建一个新的文件夹,命名为store。在store文件夹中创建一个新的文件index.js,作为Vuex的入口文件。 3. 在index.js中导入VueVuex,并创建一个新的store实例: ```javascript import { createApp } from 'vue' import { createStore } from 'vuex' const store = createStore({ // 在这里定义你的状态、mutations、actions等 }) export default store ``` 4. 在main.js中引入store实例并将其挂载到Vue应用上: ```javascript import { createApp } from 'vue' import store from './store' const app = createApp(App) app.use(store) app.mount('#app') ``` 5. 在store文件夹中创建一个新的文件module.js,用于定义模块化的状态、mutations和actions。例如: ```javascript const state = { count: 0 } const mutations = { increment(state) { state.count++ } } const actions = { incrementAsync({ commit }) { setTimeout(() => { commit('increment') }, 1000) } } export default { state, mutations, actions } ``` 6. 在store/index.js中引入模块化的文件并注册到store实例中: ```javascript import { createStore } from 'vuex' import module from './module' const store = createStore({ modules: { module // 注册模块 } }) export default store ``` 现在你已经成功配置了Vuex,并可以在组件中使用它。你可以使用`$store`对象来访问状态、提交mutations和分发actions。例如,在组件中使用状态和提交mutation的示例: ```javascript export default { computed: { count() { return this.$store.state.module.count } }, methods: { increment() { this.$store.commit('increment') } } } ``` 这是一个简单的开始使用Vuex的例子,你可以根据你的具体需求来定义更多的状态、mutations和actions。请确保在使用之前阅读Vuex的文档以获取更多详细信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DomCode

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值