【vue3】14-Vue全家桶-VueX状态管理

认识应用状态管理

什么是状态管理?

在开发中,我们会的应用程序需要处理各种各样的数据,这些数据需要保存在我们应用程序中的某一个位置,对于这些数据的管理我们就称之为是状态管理

在前面我们是如何管理自己的状态呢?

  • 在Vue开发中,我们使用组件化的开发方式;
  • 而在组件中我们定义data或者在setup中返回使用的数据,这些数据我们称之为state;
  • 模块template中我们可以使用这些数据,模块最终会被渲染成DOM,我们称之为View;
  • 在模块中我们会产生一些行为事件,处理这些行为事件时,有可能会修改state,这些行为事件我们称之为actions;

在这里插入图片描述

如今JavaScript开发的应用程序,已经变得越来越复杂了:

  • JavaScript需要管理的状态越来越多,越来越复杂;
  • 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等;
  • 也包括一些UI的状态,比如某些元素是否被选中,是否显示加载动效,当前分页;

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

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

我们是否可以通过组件数据的传递来完成呢?

  • 对于一些简单的状态,确实可以通过props的传递或者Provide的方式来共享状态;
  • 但是对于复杂的状态管理来说,显然单纯通过传递和共享的方式是不足以解决问题的,比如兄弟组件如何共享数据呢?

VueX的状态管理

管理不断变化的state本身是非常困难的:

  • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也有可能会引起状态的变化;
  • 当应用程序复杂时,state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪;

因此,我们是否可以考虑将组件的内部状态抽离出来,以一个全局单例的方式来管理呢?

  • 在这种模式下,我们的组件树构成了一个巨大的“试图View";
  • 不管在树的哪个位置,任何组件都能获取状态或者触发行为;
  • 通过定义和隔离状态管理中的各个概念,并通过强制性的规则来维护视图和状态间的独立性,我们的代码边会变得更加结构化和易于维护、跟踪;

这就是Vuex背后的基本思想,它借鉴了Flux、Redux、Elm(纯函数语言,redux有借鉴它的思想)。当然,目前Vue3的话官方更推荐使用Pinia进行状态管理,在下一篇会有专门的博客进行讲解

在这里插入图片描述

Vuex状态管理详解

Vuex的基本使用

每一个Vuex应用的核心就是store(仓库)∶

store本质上是一个容器,它包含着应用中大部分的状态(state) ;

Vuex和单纯的全局对象有什么区别呢?

  • 第一: Vuex的状态存储是响应式的
    • 当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会被更新;
  • 第二:你不能直接改变store中的状态
    • 改变store中的状态的唯一途径就显示提交(commit) mutation;
    • 这样使得我们可以方便的跟踪每一个状态的变化,从而让我们能够通过一些工具帮助我们更好的管理应用的状态;

拿下面这样一个计数器来作为示例演示vuex的使用步骤, 我们希望这个计数器的counter抽离到store仓库里面, 让多个组件共享数据:

<template>
  <div class="app">
    <h2>当前计数: {{ counter }}</h2>
  </div>
</template>

<script setup>
  import { ref } from 'vue';

  let counter = ref(0)
</script>

(1)安装

在终端执行命令行npm install vuex

(2)创建sotre对象

  1. 在src文件夹下建立states文件夹并在其中创建index.js文件
  2. 在js文件中导入createStore函数
  3. 创建store对象并导出
// 1.导入createStore函数
import { createStore } from "vuex";

// 2.创建store对象
const store = createStore({
  // state函数要求返回一个对象
  state: () => ({
  //定义计数器中使用的counter
    counter: 0
  })
})

export default store

(3)在main.js中通过插件方式安装store

import { createApp } from 'vue'
import App from './App.vue'

// 1.导入状态管理store对象
import store from "./store/index"

//const app = createApp(App)
//使用store
//app.use(store)
//app.mount('#app')
createApp(App).use(store).mount('#app')


(4)在组件中使用store,我们按照如下的方式

  1. 在模板中使用store中的counter
  2. 在options Api中使用
  3. 在setup中使用
  • 在模板中访问couter:store: $store.state.counter
<template>
  <div class="app">
    <h2>当前计数: {{ $store.state.counter }}</h2>
  </div>
</template>

  • 在options api中使用,比如computed中:
export default {
  computed: {
    storeCounter() {
      return this.$store.state.counter
    }
  }
}

在setup中使用:

<script setup>
// 在setup中使用
import { toRefs } from 'vue';
import { useStore } from 'vuex';
// 通过store获取counter
  // 1.直接解构的话会丢失响应式的
  // const { counter } = store.state.counter
  
  // 2.解构时包裹toRefs函数, 返回的是ref对象, 会保留响应式

const store = useStore()
const{counter, message}= toRefs(store.state) 

</script>

单一状态树

Vuex使用单一状态树:

  • 一个对象就包含了全部的应用层级的状态;
  • 采用的是SSOT,Single Source of Truth,也可以翻译成单一数据源;

这也意味着,每个应用将仅仅包含一个store 实例;

  • 单状态树和模块化并不冲突,后面我们会讲到module的概念;

单一状态树的优势:

  • 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难;
  • 所以Vuex也使用了单一状态树来管理应用层级的全部状态;
  • 单一状态树能够让我们最直接的方式找到某个状态的片段;
  • 而且在之后的维护和调试过程中,也可以非常方便的管理和维护;

核心概念State

组件获取状态

在前面我们已经学习过如何在组件中获取状态了。

当然,如果觉得那种方式有点繁琐(表达式过长),我们可以使用计算属性:

在这里插入图片描述

但是,如果我们有很多个状态都需要获取话,可以使用mapState的辅助函数:

  • mapState的方式一:对象类型;
  • mapState的方式二:数组类型;
  • 也可以使用展开运算符和来原有的computed混合在一起;

方式一: 数组类型

  1. 要求数组中映射的名字和state中的名字一致

  2. 如果data数据中也有相似的名称那么就会冲突

<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>

<script>
  import { mapState } from 'vuex';

  export default {
    computed: {
      // 1.数组的方式, 名称和state中的需要一一对应
      ...mapState(["name", "age", "height"])
    }
  }
</script>

方式二: 对象类型

对象类型可以自定义名称, 属性值为一个函数

<h2>{{ sName }}</h2>
<h2>{{ sAge }}</h2>
<h2>{{ sHeight }}</h2>

<script>
  import { mapState } from 'vuex';

  export default {
    computed: {
      // 对象的方式, 可以自定义名称
      ...mapState({
        // 属性值为一个函数, 固定的写法
        sName: state => state.name,
        sAge: state => state.age,
        sHeight: state => state.height
      })
    }
  }
</script>

setup中获取状态

在setup中如果我们单个获取状态是非常简单的:

  • 通过useStore拿到store后去获取某个状态即可;
  • 但是如果我们需要使用mapState的功能呢?

默认情况下,Vuex并没有提供非常方便的使用mapState的方式,在setup中使用起来还是比较复杂的:

方法一(了解):使用过程太复杂,了解即可

<h2>{{ aName }}</h2>
<h2>{{ aAge }}</h2>
<h2>{{ aHeight }}</h2>

<script setup>
  import { computed } from 'vue';
  import { mapState, useStore } from 'vuex';

  // 获取store对象
  const store = useStore()

  // 对返回的函数进行解构, 得到三个函数
  const {name, age, height} = mapState(["name", "age", "height"])

  // computed要求传入一个函数, 将解构的函数传入
  // 对解构的函数进行this绑定, 将this绑定到store上
  const aName = computed(name.bind({$store: store}))
  const aAge = computed(age.bind({$store: store}))
  const aHeight = computed(height.bind({$store: store}))

</script>


方法二:

  • 在开发中不推荐在setup中使用mapState
    • 可以通过直接对store.state进行解构
    • 包裹一层toRefs()函数让数据变成响应式
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>

<script setup>
  import { toRefs } from 'vue';
  import { mapState, useStore } from 'vuex';

  // 获取store对象
  const store = useStore()
	
  // 解构同时包裹一层toRefs函数
  const {name, age, height} = toRefs(store.state)
</script>


方法三:对mapstate进行函数封装,再进行使用:

在这里插入图片描述

核心概念Getters

getters基本使用

(1)某些属性我们可能需要经过变化后来使用,这个时候可以使用getters:

// 导入createStore函数
import { createStore } from "vuex";

// 创建store对象
const store = createStore({
    state: () => ({
        counter: 100,
        friedns: [
            { id: 111, name: "yhy", age: 19 },
            { id: 112, name: "kaisa", age: 20 },
            { id: 113, name: "vn", age: 21 }
        ]
    }),

    // 某些属性我们可能需要经过变化后来使用,这个时候可以使用getters:
    // getters对应一个对象
    getters: {
        // 1.基本使用
        // (1).将counter的两倍返回
        doubleCounter(state) {
            return state.counter * 2
        },
        //   (2)将friends中的年龄总和返回
        friendstotalAge(state) {
            return state.friedns.reduce((preValue, item) => {
                return preValue + item.age
            }, 0)
        }

    }

})

// 导出store
export default store

在模板中进行展示:

  <h2>doublecounter:{{ $store.getters.doubleCounter }}</h2>
  <h2>totalage:{{ $store.getters.friendstotalAge }}</h2> 

(2)getters可以接收第二个参数:

  • 例如返回一个name并且拼接上friends的年龄总和;
  • 其中的年龄总第二步已经计算过一次了, 我们无需重新计算, 通过参数getters可以拿到
// 导入createStore函数
import { createStore } from "vuex";

// 创建store对象
const store = createStore({
    state: () => ({
        counter: 100,
        friedns: [
            { id: 111, name: "yhy", age: 19 },
            { id: 112, name: "kaisa", age: 20 },
            { id: 113, name: "vn", age: 21 }
        ]
    }),
    // getters对应一个对象
    getters: {
        // 1.基本使用
        // (1).将counter的两倍返回
        doubleCounter(state) {
            return state.counter * 2
        },
        //   (2)将friends中的年龄总和返回
        friendstotalAge(state) {
            return state.friedns.reduce((preValue, item) => {
                return preValue + item.age
            }, 0)
        },
        
        message(state, getters) {
            //2. 在该getters属性中,想要获取其他的getters:用第二个参数即可
            return ` name:${state.name} level:${state.level} friendTotalAge:${getters.friendstotalAge} `
        },

    }

})


(3)getters的返回函数(了解)

getters中的函数本身,可以返回一个函数,那么在使用的地方相当于可以调用这个函数:

// 导入createStore函数
import { createStore } from "vuex";

// 创建store对象
const store = createStore({
    state: () => ({
        counter: 100,
        friedns: [
            { id: 111, name: "yhy", age: 19 },
            { id: 112, name: "kaisa", age: 20 },
            { id: 113, name: "vn", age: 21 }
        ]
    }),

    getters: {
        //   获取id为xxx的朋友的全部信息(这样就写死了);
        // getFriends111(state){
        //     return state.friedns.find(item => item.id ===111)
        // }
        
        // 3.getters是可以返回一个函数的,调用这个函数可以传入参数(了解)
        //   动态获取id为xxx朋友信息
        getFriendsById(state) {
            return function (id) {
                return state.friedns.find(item => item.id === id)
            }

        }

    }

})

在使用的地方可以调用getFriendsById函数:

  <!-- 根据id获取某一个朋友的信息 -->
  <!-- <h2>id为111朋友信息:{{ $store.getters.getFriends111 }}</h2> -->
  <!-- 优化为动态获取 -->
  <h2>id为111朋友信息:{{ $store.getters. getFriendsById(111) }}</h2>
  <h2>id为112朋友信息:{{ $store.getters. getFriendsById(112) }}</h2>

getters辅助函数

和state类似, getters也有一个辅助函数mapGetters

(1) Options API中使用辅助函数: 方便获取多个getters

使用方法基本上和mapState相似, 示例如下:

  <h2>{{ message }}</h2>
    <h2>{{ totalAge }}</h2>
    <h2>{{ meMessage }}</h2>
    <h2>{{ usersAge }}</h2>

  </div>
</template>

<script>
  import { mapGetters } from 'vuex'

  export default {
    computed: {
      // 1.数组的形式
      ...mapGetters(["message", "totalAge"]),
      // 2.对象的形式
      ...mapGetters({
        meMessage: "message",
        usersAge: "totalAge"
      })
    }
  }
</script>

(2)在setup中使用mapGetters

和使用mapState一样, 是非常繁琐的, 不推荐使用;使用方法和mapState基本相似, 推荐解构或者对某一个属性使用computed:

  • 可以通过直接对store.getters进行解构
  • 包裹一层toRefs()函数让数据变成响应式

在这里插入图片描述

核心概念Mutations

Mutation基本使用

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation:

  • 比如, 我们如果想要修改store中的state中的counter属性:
    • 我们并不能直接通过store.state.counter修改
    • 必须通过commit提交mutation, 在mutation中进行修改

options Api中:

methods选项中: 触发后通过this.$store.commit("mutation中的函数")提交到mutation

在这里插入图片描述

setup中则是通过拿到store对象通过store.commmit来提交

const store = createStore({
  state: () => ({
    counter: 100,
  }),
  // commit提交到mutations
  mutations: {
    increment(state) {
      state.counter++
    },
    decrement(state) {
      state.counter--
    }
  }
})

<h2>当前计数: {{ counter }}</h2>
<button @click="add">+</button>
<button @click="sub">-</button>

<script setup>
  import { useStore } from 'vuex';

  // 拿到store对象
  const store = useStore()

  function add() {
    // 通过commit提交到mutation
    store.commit("increment")
  }

  function sub () {
    store.commit("decrement")
  }
</script>

mutation携带数据

很多时候我们在提交mutation的时候,会携带一些数据,这个时候我们可以使用参数:

  • 例如修改state中的name属性时, 可以将修改的内容通过传递参数传出;
  • 传入的参数可以是对象类型
const store = createStore({
  state: () => ({
    name: "sevgilid",
  }),
  // commit提交到mutations
  mutations: {
    //1. payload接收传入的参数,动态修改
    changeName(state, payload) {
      state.name = payload
    }
    
    // 2.接收类型为对象的参数
    changeInfo(state,newInfo){
        state.name = newInfo.newName,
        state.level = newInfo.newLevel
        }

  }
})

<h2>name:{{ $store.state.name }}</h2>
<button @click="changeName">修改name</button>
<script setup>
import { toRefs } from 'vue';
import { useStore } from 'vuex';

const store = useStore()

function changeName(){
  store.commit("changeName","李二狗")
}

function changeInfo(){
  store.commit("changeInfo",{
    newName : "李大庆",
    newLevel : "No.1" 
  })
}
</script>

Mutation常量类型

  • commit提交和mutation中的名称是需要保持一致的, 由于这个名称又不会更改;
  • 所以开发中, 我们通常会给这些名称定义一个常量, 并将它放到store文件夹下独立的的js文件中去

(1)定义常量:mutation_type.js

export const CHANGE_INFO = "CHANGE_INFO"

(2)使用常量定义mutation:

// 导入常量
import { CHANGE_INFO } from "./mutation_types";

const store = createStore({
  state: () => ({
    name: "sevgilid",
    level: "A",
  }),
  mutations: {
    // 使用常量定义mutation
    [CHANGE_INFO](state, newInfo) {
        state.name = newInfo.newName,
        state.level = newInfo.newLevel
    }
  }
})

(3)使用常量提交mutation:

// 导入常量
import { CHANGE_INFO } from "./store/mutation_types"

function changeInfo() {
  // 参数传入一个对象
  store.commit([CHANGE_INFO], {
    newName : "李大庆",
    newLevel : "No.1" 
  })
}

Mutation辅助函数

我们也可以借助于辅助函数,帮助我们快速映射到对应的方法中:

(1) Options API中的使用:

<h2>当前计数: {{ counter }}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>

<h2>{{ $store.state.name }}</h2>
<h2>{{ $store.state.age }}</h2>
<h2>{{ $store.state.height }}</h2>
<button @click="changeInfo({name: '李大牛', age: 21, height: 1.83})">
  修改info
</button>

<script>
  import { mapMutations } from "vuex";
  import { CHANGE_INFO } from "./store/mutation_types"

  export default {
    methods: {
      ...mapMutations(["increment", "decrement", CHANGE_INFO])
    }
  }
</script>

(2) Setup中同样不推荐使用mapMutation

mutation重要原则

一条重要的原则就是要记住mutation必须是同步函数

  • 这是因为devtool工具会记录mutation的日记;
  • 每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照;
  • 但是在mutation中执行异步操作,就无法追踪到数据的变化;

所以Vuex的重要原则中要求 mutation必须是同步函数;

但是如果我们希望在Vuex中发送网络请求的话需要如何操作呢?

  • 有时候我们确实想要在Vuex中发送网络请求, 比如发送网络请求得到的数据是一些状态;
  • 我们没必要在组件中发送网络请求再放到store中, 我们可以直接在store中发送网络请求;
  • 那么遇到这种情况, 就是我们接下来要学习的Actions核心, Actions是专门处理异步操作的地方, 我们的异步代码都需要在Actions中处理

核心概念Actions

Actions基本使用

Action类似于mutation,不同在于:

  • Action提交的是mutation,而不是直接变更状态;
  • Action可以包含任意异步操作;
mutations: {
  increment(state) {
    state.counter++
  }
},
actions: {
  incrementAction(context) {
    // action中修改state需要提交mutation
    context.commit("increment")
  }
}

这里有一个非常重要的参数context:

  • context是一个和store实例均有相同方法和属性的context对象;
  • 所以我们可以从其中获取到commit方法来提交一个mutation,或者通过context.statecontext.getters来获取state和getters;
actions: {
  incrementAction(context) {
    // 提交mutation
    context.commit("increment")
    // 获取state中的状态
    context.state.name
    // 获取getter中的数据
    context.getters.doubleCounter
  }
}

但是为什么它不是store对象呢?这个等到我们讲Modules时再具体来说;

Actions分发操作

(1)Options中Action的派发(分发)

如何使用action呢?进行action的派发:

  • 派发使用的是 store 上的dispatch函数
<h2>{{ counter }}</h2>
<button @click="add">按钮</button>

<script>
  export default {
    methods: {
      add() {
        // 分发到Action中
        this.$store.dispatch("incrementAction")
        console.log()
      }
    }
  }
</script>

同样的,它也可以携带我们的参数, 例如修改我们刚刚的Info

  • 定义Action
actions: {
  // 接收参数payload提交到mutation
  changeNameAction(context, payload) {
    context.commit("changeName", payload)
  },
  //对象类型也可以
  changeInfoAction(context, payload) {
    context.commit(CHANGE_INFO, payload)
  }
}

  • 携带参数分发
<h2>{{ name }}</h2>
<button @click="changeName">改变Name</button>

<script>
  export default {
    methods: {
      changeName() {
        // 分发时携带参数
        this.$store.dispatch("changeNameAction", "罗三炮")
      },
      //参数可以是对象类型
      changeInfo() {
        this.store.dispatch("changeInfoAction", {
          name: "李大牛",
          age: 23,
          height: 1.85
        })
      }

  }
</script>

(2)setup中Action的派发(分发)

<script setup>
  import { useStore } from 'vuex';

  import { toRefs } from 'vue';

  const store = useStore()

  const {counter, name, age, height} = toRefs(store.state)

  // 分发incrementAction
  function add() {
    store.dispatch("incrementAction")
  }

  // 分发changeNameAction
  function changeName() {
    store.dispatch("changeNameAction", "罗三炮")
  }

  // 分发changeInfoAction
  function changeInfo() {
    store.dispatch("changeInfoAction", {
      name: "王老五",
      age: 40,
      height: 1.56
    })
  }
</script>

Actions辅助函数

  • action也有对应的辅助函数:

    • 对象类型的写法;
    • 数组类型的写法;

数组类型的写法:

<h2>{{ counter }}</h2>
<button @click="incrementAction">按钮</button>
<h2>{{ name }}</h2>
<button @click="changeNameAction('张三疯')">改变Name</button>

<script>
  import { mapActions } from 'vuex';

  export default {
    methods: {
      // 数组的写法
      ...mapActions(["incrementAction", "changeNameAction"])
    }
  }
</script>

对象类型的写法:

<h2>{{ counter }}</h2>
<button @click="add">按钮</button>
<h2>{{ name }}</h2>
<button @click="changeName('李二狗')">改变Name</button>

<script>
  import { mapActions } from 'vuex';

  export default {
    methods: {
      ...mapActions({
        add: "incrementAction",
        changeName: "changeNameAction"
      })
    }
  }
</script>

Setup中同样不推荐使用mapAction


Actions异步操作

Action 通常是异步的,那么如何知道 action 什么时候结束呢?

我们可以通过让action返回Promise,在Promise的then中来处理完成后的操作;

演示代码:

(1)将网络请求的数据请求到vuex中:

const store = createStore({
  state: () => ({
    // 1.用于存放网络请求的数据
    banners: [],
    recommends: []
  }),
  mutations: {
    // 2.定义mutation用于修改state的banners和recommends
    changeBanners(state, payload) {
      state.banners = payload
    },
    changeRecommends(state, payload) {
      state.recommends = payload
    }
  },
  actions: {
    // 3.定义发生网络请求的Action
    fetchHomeMulitidataAction(context) {
      // 发生网络请求
      fetch("http://123.207.32.32:8000/home/multidata").then(res => {
        return res.json()
      }).then(data => {
        // 修改state中banner的数据, 提交mutation
        context.commit("changeBanners", data.data.banner.list)
        // 修改state中recommends的数据, 提交mutation
        context.commit("changeRecommends", data.data.recommend.list)
      })
    }
  }
})

(2)请求到数据后可以获取vuex中的数据进行展示

<template>
  <div class="Home">
    <!-- 2.展示数据 -->
    <ul>
        <li v-for="item in $store.state.banners" :key="item.acm">
          {{ item.title }}
        </li>
    </ul>
  </div>
</template>

<script setup>
  import { useStore } from 'vuex';

  const store = useStore()

  // 1.告知Action发生网络请求
  store.dispatch("fetchHomeMulitidataAction")
</script>

核心概念Modules

modules基本使用

什么是Module?

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

在这里插入图片描述

代码示例:

(1)将刚刚home组件请求的数据单独抽离到一个home.js文件中, js文件返回一个对象

export default {
  state: () => ({
    // 网络请求的数据
    banners: [],
    recommends: []
  }),
  mutations: {
    // 定义mutation用于修改state的状态
    changeBanners(state, payload) {
      state.banners = payload
    },
    changeRecommends(state, payload) {
      state.recommends = payload
    }
  },
  actions: {
    fetchHomeMulitidataAction(context) {
      fetch("http://123.207.32.32:8000/home/multidata").then(res => {
        return res.json()
      }).then(data => {
        // 修改state中banner的数据, 提交mutation
        context.commit("changeBanners", data.data.banner.list)
        // 修改state中recommends的数据, 提交mutation
        context.commit("changeRecommends", data.data.recommend.list)
      })
    }
  }
}

(2)再将抽离出去的模块导入store对象

// 1.导入抽离出去的模块
import HomeModule from "../module/home"

const store = createStore({
  state: () => ({}),
  getters: {},
  mutations: {},
  actions: {},
  modules: {
    //模块名: 模块对象
    home: HomeModule
  }
})

(3)展示state状态, 要从模块中获取;

而getter、mutation、action不需要, 因为他们默认会被合并到根模块中去, 一般情况下都可以直接获取

<!-- 展示数据 -->
<ul>
    <!-- $store.state.模块.数据:需要加上"home" -->
    <li v-for="item in $store.state.home.banners" :key="item.acm">
      {{item.title}}
    </li>
</ul>

  • 对于模块内部的 mutation 和 getter,接收的第一个参数的state是模块的局部状态对象

释义:就是这里面的state不是index.js里的state,而是在当前文件里的state,如果要用根模块中的数据,要用第三个参数rootState来获取

组件中使用:默认是不用加模块名的,但是这样就是在全局去找,因为模块也是在全局的

在这里插入图片描述

  • 所以在模块里命名要特别小心,注意不要和store里的重复了;
  • 一旦重复,调用的就是store里的,而非模块里的,因为在调用的时候是不用加模块的,所以这个时候就有命名空间来解决这个问题

module命名空间

默认情况下,模块内部的getter、action和mutation仍然是注册在全局的命名空间中的:

  • 这样使得多个模块能够对同一个 action 或 mutation 作出响应, 这并不是我们想要的结果, 导致在模块中我们取名需要非常小心避免重复;
  • Getter 同样也默认注册在全局命名空间;

如果我们希望模块具有更高的封装度和复用性, 解决这个尴尬的问题

  • 可以添加 namespaced: true 的方式使其成为带命名空间的模块
  • 当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名
export default {

  // 带命名空间的模块
  namespaced: true,

  state: () => ({
    count: 10,
    // 网络请求的数据
    banners: [],
    recommends: []
  }),
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  },
  mutations: {
    // 定义mutation用于修改state的状态
    changeBanners(state, payload) {
      state.banners = payload
    },
    changeRecommends(state, payload) {
      state.recommends = payload
    }
  },
  actions: {
    fetchHomeMulitidataAction(context) {
      fetch("http://123.207.32.32:8000/home/multidata").then(res => {
        return res.json()
      }).then(data => {
        // 修改state中banner的数据, 提交mutation
        context.commit("changeBanners", data.data.banner.list)
        // 修改state中recommends的数据, 提交mutation
        context.commit("changeRecommends", data.data.recommend.list)
      })
    }
  }
}

以为加上命名空间后, 所以我们需要按照如下的方式获取模块中的getter、action和mutation

<!-- 获取home模块中的getter -->
<h2>{{ $store.getters["home/doubleCount"] }}</h2>
// 向home中的action分发
store.dispatch("home/fetchHomeMulitidataAction")

module修改或派发根组件

因此context并不等于store, 在不同模块中的context代表是不同的

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值