vuex

专门在vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

使用场景:

  • 多个组件依赖于同一状态
  • 来自不同组件的行为需要变更同一状态

搭建Vuex环境

  1. 创建文件:src/store/index.js
    ```javascript // 用于创建vuex中最核心的store

// 引入vuex import Vue from ‘vue’ import Vuex from ‘vuex’

// 使用Vuex插件 Vue.use(Vuex)

// 准备actions,用于响应组件中的动作 const actions = {}

// 准备mutations,用于操作数据(state) const mutations = {}

// 准备state,用于存储数据 const state = {}

// 创建并暴露store export default new Vuex.Store({ actions, // actions:actions mutations, state });

  1. 2. `main.js`中创建vm时传入`store`配置项
  2. ```javascript
  3. import Vue from 'vue'
  4. import App from './App.vue'
  5. // 引入store,因为store写的是index.js,所以此处不需要写完整的./store/index.js路径
  6. import store from './store'
  7. new Vue({
  8. render: h => h(App),
  9. store, // 使用vuex插件之后,就可以使用store配置项了
  10. }).$mount('#app')

此时,Vue实例对象vm和所有的组件实例对象vc都可以通过 this.$store获取到store对象,通过store的dispatch('xxx', value)调用actions中配置的方法,通过store的commit('xxx',value)调用mutations中配置的方法,通过state获取到state中配置的具体变量。

Vuex的一般流程为:

  1. 组件中通过 this.$store.dispatch('xxx', value) 调用 actions中配置的方法
  2. actions的方法中,通过context.commit('xxx', value) 调用 mutations中配置的方法
  3. mutations的方法中,对state中的变量进行修改操作

vuex.png

actions可以理解为饭店的服务员:组件调用dispatch告诉actions服务员点菜。服务员可以进行一些判断逻辑,比如客户是否有忌口、厨房是否还有原材料等(发送ajax去菜市场买菜也在这里处理)

mutations可以理解为饭点的厨师:actions中调用commit通知厨师做具体的菜。厨师对state中具体的原材料变量进行处理。

如果actions中没有什么判断逻辑,只是单纯的调用了commit通知厨师,那么就可以组件中通过commit直接通知厨师做菜。

虽然actions中也可以对state进行操作做菜,但是不推荐这么做。因为页面控制台的Vuex监控插件只会监控mutations对state的改变,不会监控actions。

示例

在index.js文件编写actions、mutations、state:

  1. // .......
  2. // 准备actions,用于响应组件中的动作
  3. const actions = {
  4. // context是一个mini版的store,拥有store的dispatch、commit等相关方法
  5. jia(context, value) {
  6. context.commit('JIA', value);
  7. },
  8. jian(context, value) {
  9. context.commit('JIAN', value);
  10. },
  11. jiaOdd(context, value) {
  12. if(context.state.sum % 2) {
  13. context.commit('JIA', value)
  14. }
  15. },
  16. jiaWait(context, value) {
  17. setTimeout(() => {
  18. context.commit('JIA', value)
  19. }, 500);
  20. },
  21. demo1(context, value) {
  22. // actions中的方法,可以通过dispatch继续调用其他方法。
  23. context.dispatch('demo2', value)
  24. },
  25. demo2(context,value) {
  26. context.commit('JIA', value)
  27. }
  28. }
  29. // 准备mutations,用于操作数据(state)
  30. const mutations = {
  31. // mutations内部的方法名一般用大写
  32. JIA(state, value) {
  33. state.sum += value
  34. },
  35. JIAN(state, value) {
  36. state.sum -= value
  37. }
  38. }
  39. // 准备state,用于存储数据
  40. const state = {
  41. sum:0
  42. }
  43. // .........

组件中调用相关方法:

  1. <template>
  2. <div>
  3. <!-- 读取Vuex中的数据 -->
  4. <h2>当前求和为:{{$store.state.sum}}</h2>
  5. <select v-model.number="n">
  6. <option value="1">1</option>
  7. <option value="2">2</option>
  8. <option value="3">3</option>
  9. </select>
  10. <button @click="increment">+</button>
  11. <button @click="decrement">-</button>
  12. <button @click="incrementOdd">当前求和为奇数再加</button>
  13. <button @click="incrementWait">等一等再加</button>
  14. </div>
  15. </template>
  16. <script>
  17. export default {
  18. name:'Count',
  19. data() {
  20. return {
  21. n:1, // 用户选择的数字
  22. }
  23. },
  24. methods: {
  25. increment() {
  26. // this.$store.dispatch('jia', this.n);
  27. // actions中没有其他逻辑,只是调用commit。那么就可以在组件中通过commit直接调用mutations中的方法
  28. this.$store.commit('JIA', this.n)
  29. },
  30. decrement() {
  31. // this.$store.dispatch('jian', this.n);
  32. this.$store.commit('JIAN', this.n)
  33. },
  34. incrementOdd() {
  35. // 通过dispatch调用actions中的方法
  36. this.$store.dispatch('jiaOdd', this.n)
  37. },
  38. incrementWait() {
  39. this.$store.dispatch('jiaWait', this.n)
  40. }
  41. },
  42. }
  43. </script>

getters

类似于计算属性,位于store中。当state中的数据需要加工后再使用时,可以使用getters加工。

这个属性是非必须的,可以根据需要添加。

示例:

在index.js中配置getters

  1. // 定义getters
  2. const getters = {
  3. bigSum(state) { // 可以接收到state
  4. return state.sum * 10; // 需要有返回值
  5. }
  6. }
  7. // 创建并暴露store
  8. export default new Vuex.Store({
  9. actions,
  10. mutations,
  11. state,
  12. getters // 将getters配置到store中
  13. });

在页面使用getters:

  1. <template>
  2. <div>
  3. <h2>当前求和为:{{$store.state.sum}}</h2>
  4. <!-- 通过$store.getters获取到getters配置的属性 -->
  5. <h2>当前求和放大10倍为:{{$store.getters.bigSum}}</h2>
  6. </div>
  7. </template>

4个map方法的使用

mapState可以用于帮我们映射state中的数据为计算属性。

mapGetters用于帮我们映射getters中的数据为计算属性。

mapActions用于帮我们生成与actions对话的方法,即包含$store.dispatch(xxx)的函数

mapMutations用于帮我们生成与mutations对话的方法,即包含$store.commit的函数

如果要从state中获取很多属性,直接使用计算属性的写法为:

  1. <template>
  2. <div>
  3. <h2>求和:{{he}}</h2>
  4. <h2>学校:{{xuexiao}}</h2>
  5. <h2>学科:{{xueke}}</h2>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name:'Count',
  11. computed: {
  12. he() {
  13. return this.$store.state.sum;
  14. },
  15. xuexiao() {
  16. return this.$store.state.school;
  17. },
  18. xueke() {
  19. return this.$store.state.subject;
  20. }
  21. }
  22. }
  23. </script>

每次都要写this.$store.state.比较繁琐,可以使用mapState进行简写:

  1. <script>
  2. // 引入mapState
  3. import {mapState} from 'vuex'
  4. export default {
  5. name:'Count',
  6. computed: {
  7. // mapState是一个对象,需要使用解构语法对其属性进行解构
  8. // 此时就相当于在计算属性中定义了he、xuexiao、xueke这些计算属性,冒号后面为映射到的state中的属性
  9. ...mapState({he:'sum', xuexiao:'school', xueke:'subject'})
  10. }
  11. }
  12. </script>

特别的,如果计算属性和state属性名相同,可以使用数组简写:

  1. <script>
  2. // 引入mapState
  3. import {mapState} from 'vuex'
  4. export default {
  5. name:'Count',
  6. computed: {
  7. // ...mapState({sum:'sum', school:'school', subject:'subject'})
  8. // 数组简写
  9. ...mapState(['sum', 'school', 'subject'])
  10. }
  11. }
  12. </script>

同样的,如果要获取getters中的属性,可以使用mapGetters进行简写:

  1. <script>
  2. // 引入mapState、mapGetters
  3. import {mapState,mapGetters} from 'vuex'
  4. export default {
  5. name:'Count',
  6. computed: {
  7. ...mapState(['sum', 'school','subject']),
  8. // bigSum() {
  9. // return this.$store.getters.bigSum
  10. // }
  11. // 简写为
  12. // ...mapGetters({bigSum:'bigSum'})
  13. // 或者简写为
  14. ...mapGetters(['bigSum'])
  15. }
  16. }
  17. </script>

如果要调用actions中的dispatch方法,直接在methods中写法为:

  1. <template>
  2. <div>
  3. <button @click="increment">+</button>
  4. <button @click="decrement">-</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. name:'Count',
  10. data() {
  11. return {
  12. n:1,
  13. }
  14. },
  15. methods: {
  16. incrementOdd() {
  17. // 通过dispatch调用actions中的方法
  18. this.$store.dispatch('jiaOdd', this.n)
  19. },
  20. incrementWait() {
  21. this.$store.dispatch('jiaWait', this.n)
  22. }
  23. }
  24. }
  25. </script>

可以使用mapActions简写$store.dispatch

  1. <template>
  2. <div>
  3. <!-- 使用mapActions时,需要在调用方法时直接将参数传进去 -->
  4. <button @click="increment(n)">+</button>
  5. <button @click="decrement(n)">-</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name:'Count',
  11. data() {
  12. return {
  13. n:1,
  14. }
  15. },
  16. methods: {
  17. ...mapActions({incrementOdd:'jiaOdd', incrementWait:'jiaWait'}) // 对象写法
  18. }
  19. }
  20. </script>

如果组件中方法名和actions中方法名同名,则在mapActions中同样可以使用数组写法。

同样的,可以使用mapMutations简写$store.commit

  1. <template>
  2. <div>
  3. <!-- 使用mapMutations时,需要在调用方法时直接将参数传进去 -->
  4. <button @click="incrementOdd(n)">当前求和为奇数再加</button>
  5. <button @click="incrementWait(n)">等一等再加</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name:'Count',
  11. data() {
  12. return {
  13. n:1,
  14. }
  15. },
  16. methods: {
  17. // mapMutations同样有对象写法、数组写法
  18. ...mapMutations({increment:'JIA', decrement:'JIAN'}),
  19. }
  20. }
  21. </script>

mapActions和mapMutations使用时,若需要传递参数:需要在模板中绑定事件时传递好参数,否则参数是事件对象。

模块化和命名空间

目的:让代码更好维护,让多种数据分类更加明确。

示例:修改store的index.js

  1. // 定义count组件用到的
  2. const countOptiosn = {
  3. namespaced: true,
  4. actions: {
  5. jia(context, value) {
  6. // console.log('actions的jia调用了', context, value);
  7. context.commit('JIA', value);
  8. },
  9. jian(context, value) {
  10. context.commit('JIAN', value);
  11. },
  12. jiaOdd(context, value) {
  13. if(context.state.sum % 2) {
  14. context.commit('JIA', value)
  15. }
  16. },
  17. jiaWait(context, value) {
  18. setTimeout(() => {
  19. context.commit('JIA', value)
  20. }, 500);
  21. }
  22. },
  23. mutations: {
  24. JIA(state, value) {
  25. // console.log('mutations的JIA调用了', state, value)
  26. state.sum += value
  27. },
  28. JIAN(state, value) {
  29. state.sum -= value
  30. }
  31. },
  32. state: {
  33. sum:0,
  34. school: '庞各庄小学',
  35. subject: '数学'
  36. },
  37. getters: {
  38. bigSum(state) {
  39. return state.sum * 10;
  40. }
  41. }
  42. }
  43. // 定义person组件用到的
  44. const personOptions = {
  45. namespaced: true,
  46. actions: {
  47. addLiSi(context, value) {
  48. if(value.name === '李四') {
  49. context.commit('addUser', value);
  50. } else {
  51. alert('只能输入李四进行添加')
  52. }
  53. }
  54. },
  55. mutations: {
  56. addUser(state, value) {
  57. state.personList.unshift(value);
  58. }
  59. },
  60. state: {
  61. personList: [{id:'001', name:'张三'}]
  62. },
  63. getters: {
  64. }
  65. }
  66. // 使用Vuex插件
  67. Vue.use(Vuex)
  68. // 创建并暴露store
  69. export default new Vuex.Store({
  70. // 使用modules进行暴露
  71. modules: {
  72. countOptions: countOptions,
  73. personOptions: personOptions
  74. }
  75. });

在组件中使用map读取命名空间的数据:

  1. export default {
  2. name:'Count',
  3. methods: {
  4. // 调用countOptions命名空间的mutations
  5. ...mapMutations('countOptions',{increment:'JIA', decrement:'JIAN'}),
  6. // 调用countOptions命名空间的actions
  7. ...mapActions('countOptions',{incrementOdd:'jiaOdd', incrementWait:'jiaWait'})
  8. },
  9. computed: {
  10. // 读取countOptions命名空间的state
  11. ...mapState('countOptions',['sum', 'school','subject']),
  12. // 读取countOptions命名空间的getters
  13. ...mapGetters('countOptions',{bigSum:'bigSum'})
  14. }
  15. }

在组件中直接读取命名空间的数据:

  1. export default {
  2. name: 'Person',
  3. data() {
  4. return {
  5. name: ''
  6. }
  7. },
  8. computed: {
  9. personList() {
  10. // 读取personOptions命名空间的state数据
  11. return this.$store.state.personOptions.personList;
  12. },
  13. countSum() {
  14. // 读取countOptions命名空间的getters数据
  15. return this.$store.getters['countOptions/bigSum']
  16. }
  17. },
  18. methods: {
  19. addUser() {
  20. // 调用personOptions命名空间的mutations方法
  21. this.$store.commit('personOptions/addUser', {id:nanoid(), name: this.name});
  22. this.name = '';
  23. },
  24. addLiSi() {
  25. // 调用personOptions命名空间的actions方法
  26. this.$store.dispatch('personOptions/addLiSi',{id:nanoid(), name: this.name});
  27. this.name = '';
  28. }
  29. }
  30. }