Vuex—可以实现所有组件之间的通信

535 阅读4分钟

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

image.png

一、安装

vue2版本不能直接用npm i vuex,因为vuex已经更新到4版本了,只能用于vue3,vue2安装vuex4会报错

所以vue2版本安装vuex命令如下:

npm i vuex@3

vue2用vuex3,vue3用vuex4。

二、编写store

vuex通过store实现对actions、mutations、state的控制

在src目录下创建store文件夹->index.js文件并写入以下内容(以加操作为例)

// 该文件用于创建vuex中最核心的store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 准备actions——用于响应组件中的动作
const actions = {
 //context(上下文)->store的一部分 value->dispatch传过来的值
   jia (context, value) {
     // console.log('action中的jia被调用', context, value)
     context.commit('JIA', value)
   }
}
// 准备mutations——用于操作数据
const mutations = {
// state->state中的值 value->actions传过来的参数
  JIA (state, value) {
    // console.log('mutations中的JIA被调用', state, value)
    state.sum += value
  }
}
// 准备state——用于存储数据
const state = {
  sum: 0
}

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

三、在main.js中导入store并引用

import store from '@/store'
new Vue({
  render: h => h(App),
  store
}).$mount('#app')

注意

1、各个部分的作用

  • actions——用于响应组件中的动作

  • mutations——用于操作数据

  • state——用于存储数据

2、注意Vuex使用的位置

如果将Vue.use(Vuex)放到main.js中,会出现[vuex] must call Vue.use(Vuex) before creating a store instance错误。就算将store的导入放到最后也会出现此错误,因为main.js会先找到并执行import。所以必须将Vue.use(Vuex)放到store的index.js中。

3、各个阶段可以拿到的数据

actions

actions中可以调用dispatch和commit方法,得到并可以修改state中的数组

image.png

同一个actions中调用方法,使用store.dispatch方法 mutations

mutations中可以得到并修改state中的数据

image.png

4、为什么要照着他们各自的功能写,明明都可以修改state中的数据?

  • 在actions里面操作数据的缺点:开发者工具失效,因为Devtools是和Mutations联系在一起,没有Mutations的存在Devtools无法生效。

  • 把业务逻辑直接写在组件函数中而不写到actions中的缺点:如果业务逻辑复杂(比如涉及验证、调用接口之类的),会写很多重复代码,而且没必要。

其他注意点

  • 组件中读取vuex中的数据:$store.state.数据名称

  • 组件中修改vuex中的数据:$store.dispatch('actions中的方法名',数据)或$store.commit('mutations中的方法名',数据)

  • 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit。

其他配置

getters—用于对数据进行加工

getters中的方法可以拿到state中的数据并进行加工,getters中的数据也可以在vue Devtools中看到。如果数据处理复杂或者会多次复用可以考虑使用getters。

定义getters并配置到store中

// 准备getters——用于对数据进行加工
const getters = {
  bigSum (state) {
    return state.sum * 10
  }
}
// 创建并暴露store
export default new Vuex.Store({
  actions,
  mutations,
  state,
  getters
})

页面中使用getters中的数据

 <h1>当前求和增大十倍为:{{$store.getters.bigSum}}</h1>

image.png

组件中读取数据:$store.getters.函数名

Vuex中的state与getters的关系等价于组件中data与computed的关系

四个map方法的使用

为什么要引入map方法?

在组件中使用store中的数据必须加上前缀$store.state

  <h1>当前求和为:{{$store.state.sum}}</h1>
  <h1>当前求和增大十倍为:{{$store.getters.bigSum}}</h1>
  <h1>我在{{$store.state.school}}学习{{$store.state.subject}}</h1>

如何直接在插值表达式中写数据名?可以通过computed属性设置

computed: {
    sum () {
      return this.$store.state.sum
    },
    school () {
      return this.$store.state.school
    },
    subject () {
      return this.$store.state.subject
    },
    bigSum () {
      return this.$store.getters.bigSum
    }
  }

但是$store.state前缀还是需要重复书写,于是vuex中提供了两种方法—mapStatemapGetters

组件方法中可能多次使用store的commit和dispatch方法

    add () {
      this.$store.commit('JIA', this.n)
    },
    sub () {
      this.$store.commit('SUB', this.n)
    },
    addOdd () {
      this.$store.dispatch('jiaOdd', this.n)
    },
    addWait () {
      this.$store.dispatch('jiaWait', this.n)
    }

可以利用mapMutationsmapActions方法进行简化

mapState方法——映射state中的数据为计算属性

import { mapState} from 'Vuex'

mapState方法通过key:value(组件中的数据:state中的数据)实现映射,生成的是一个对象/数组,所以添加到computed中需要展开。

  • 对象形式
 ...mapState({ he: 'sum', xuexiao: 'school', xueke: 'subject' })
  • 数组形式 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组
...mapState(['sum', 'school', 'subject'])

注意:value如果是字符串必须加引号,如果不加引号则表示变量。所以对象形式中不能直接写mapState({sum,school,subject})

mapGetters方法——映射getters中的数据为计算属性

通过key:value(组件中的数据:getters中的函数名)实现映射

import { mapGetters } from 'Vuex'

使用方法和mapState类似

  • 对象形式
...mapGetters({bigSum: 'bigSum'})
  • 数组形式
...mapGetters(['bigSum'])

注意:使用mapState/mapGetters方法生成的computed属性不会出现在Devtools的computed结点下,而是在vuex bindings结点下

image.png

mapActions方法—生成与actions对话的方法,即包含$store.dispatch(xxx)的函数

通过key:value(组件中的方法:actions中的方法)实现映射

import { mapActions } from 'Vuex'
  • 对象形式
methods: {...mapActions({ addOdd: 'jiaOdd', addWait: 'jiaWait' })}

mapMutations方法生成与mutations对话的方法,即包含$store.commit(xxx)的函数

通过key:value(组件中的方法:mutations中的方法)实现映射

import { mapMutations } from 'Vuex'
  • 对象形式
methods: {...mapMutations({ add: 'JIA', sub: 'SUB' })}

mapActions和mapMutations方法的数组形式和mapState方法一致。

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

<button @click="add(n)">+</button>

方法不传参但需要用到参数,会出现类似如下错误:

image.png

模块化

将一个业务对象的业务封装到一起,另外创建一个js文件导出,store文件中引入。

例如,创建一个person.js文件(用于操作人员相关业务),文件结构为:

export default {
  namespaced: true,
  actions: {},
  mutations: {},
  state: {},
  // 注意:getters中的state是局部的state,不需要通过分类名获取
  getters: {}
}

store文件中导入并引用

import personOptions from './person'
export default new Vuex.Store({
  modules: {
    personAbout: personOptions
  }
})

想要获取相应的数据可以通过$store.state.personAbout.变量名获取,也可以通过map方法简化。

想要通过map方法简化,第一个参数需要指定分类,而第一个参数指定分类,必须给每一个配置项配置一个命名空间namespaced: true,这样modules中的分类名才能被mapState所识别。

否则出现类似如下错误:

image.png

利用两个业务对象举例开启命名空间后组件如何读取/使用store中的数据/方法

export default new Vuex.Store({
  modules: {
    countAbout: countOptions,
    personAbout: personOptions
  }
})

1、开启命名空间后,组件中读取state数据:

//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取
...mapState('countAbout',['sum','school']),

2、开启命名空间后,组件中读取getters数据:

//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取
...mapGetters('countAbout',['bigSum']),

3、开启命名空间后,组件中调用dispatch

//方式一:自己直接dispatch
this.$store.dispatch['personAbout/addPersonWang',person]
//方式二:借助mapActions
...mapActions('countAbout',{add:'jiaOdd'}),

3、开启命名空间后,组件中调用commit

//方式一:自己直接commit
this.$store.commit['personAbout/addPersonWang',person]
//方式二:借助mapMutations
...mapMutations('countAbout',{add:'JIA'}),

注意:使用map方法时将分类名作为第一个参数,而自己直接读取 getters中的数据、调用commit、dispatch时第一个参数是'分类名/方法名',获取state中的数据可以直接this.$store.state.分类名.变量名