Vue(十二) Vuex、四个map方法的使用、Vuex模块化+namespace命名空间

一、Vuex

Vuex是一个Vue插件,用来实现集中式状态(数据)管理。简单来说就是,当多个组件需要读写同一数据时。使用vuex来管理这个数据,会更方便多个组件读写。
在这里插入图片描述
原理图:
在这里插入图片描述

前言:求和案例

接下来都是结合这个求和案例来说明vuex的使用
在这里插入图片描述
Count组件的点击事件分别为:

  <button @click="increment">+</button>
  <button @click="decrement">-</button>
  <button @click="incrementOdd">当前和为奇数再加</button>
  <button @click="incrementWait">等一等再加</button>

对应的监听事件为:

// data中存有sum和n两个属性;sum是求和结果,n是用户在下拉框中选择的数字
    increment () {
      this.sum = this.sum + this.n
    },
    decrement () {
      this.sum = this.sum - this.n
    },
    incrementOdd () {
      if (this.sum % 2 != 0) {
        this.sum = this.sum + this.n
      }
    },
    incrementWait () {
      setTimeout(() => {
        this.sum = this.sum + this.n
      }, 1000)
    }

1. 搭建Vuex环境

1.安装vuex

  • vue2只能使用vuex3版本:npm i vuex@3
  • vue3只能使用vuex4版本:npm i vuex@4

2.导入并使用vuex

	import Vuex from 'vuex'
	Vue.use(Vuex)

有这两句代码之后,vue实例里就可以写store配置项。写了store配置项,所有的vc和vm都能够接触到$store

3.创建store文件:src/store/index.js

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
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,
	mutations,
	state
})
  1. main.js中创建vm时传入store配置项
//引入store
	import store from './store'
	//创建vm
	new Vue({
		el:'#app',
		render: h => h(App),
		store
	})

问:为什么要在store/index.js文件里执行Vue.use(Vuex),而不是在main.js文件:

答:因为规定创建store实例之前,必须执行Vue.use(Vuex)这条语句。vue在解析main.js时,首先将所有import的文件先走一遍,再执行其他语句。因此,即使Vue.use(Vuex)写在第三行的import store from './store'之前,也是先进入store文件,创建store实例,然后再执行Vue.use(Vuex)。所以干脆在store/index.js里引入。

2. 基本使用

1.初始化数据、配置actions、配置mutations,操作store文件index.js

// index.js,方法简单写了几个,没全写
...
// 初始化数据——state用于存储数据
const state = {
  sum: 0
}

// 准备actions——用于响应组件中的动作
const actions = {
  add (context, value) {
   // 这里未进行任何逻辑处理,所以组件可直接调用commit,而不是调用dispatch先进入到actions
    context.commit('ADD', value)
  },
  addWait (context, value) {
    setTimeout(() => {
      context.commit('ADD', value)
    }, 1000)
  }
}
// 准备mutations——用于操作数据(state)
const mutations = {
  ADD (state, value) {
    state.sum += value
  }
}

2.组件中修改vuex中的数据

// Count.vue里监听事件
increment () {
  this.$store.dispatch('add', this.n)
},

3.组件中读取vuex中的数据:

<!--(插值表达式中不需要写this)-->
 <h1>当前求和为{{ $store.state.sum }}</h1>

4.如果没有其他逻辑,组件也可以越过actions直接给mutations发送消息

// Count.vue里监听事件
increment () {
  this.$store.commit('ADD', this.n)
},

5.逻辑操作一般都写在actions里面

3. 常见疑惑

  • 问: action里的函数参数为(context,value),为什么不直接给commit,还要给一个context
    答: 上下文context里包含这些内容
    在这里插入图片描述
    先只看熟悉的这几个。
    state:actions在进行业务逻辑处理时,有可能会用到state里的数据进行处理。
    dispatch:actions里,若某一个操作包含很多业务逻辑,则可能会处理完一部分后,dispatch给actions里的另一个函数继续进行逻辑处理。
    所以,只包含commit不够周到

  • 问: 业务逻辑为什么要写在actions里边,直接写在组件的点击事件里边不行吗
    答: 当业务逻辑十分复杂(比如核实发票信息之类的)时,逻辑写在actions里,其他人需要核实发票信息时直接用这套逻辑就好了。如果写在点击事件里,每个人都写一遍这套逻辑,代码冗余量提升。所以写在actions里可以减少代码冗余。

4. getters

1.概念 :当state中的数据需要经过加工后再使用时,可以使用getters加工。(有点类似于组件中的computed)

2.在storge文件中添加配置

...
const getters = {
  bigSum (state) {
    return state.sum * 10
  }
}
...
export default new Vuex.Store({
  ...
  getters
})

3.组件中读取getters里的数据 $store.getters.bigSum

5. 四个map方法的使用

假设在store/index.js中:

// 准备state——用于存储数据
const state = {
  sum: 0,
  // 新增两个属性
  name: 'tom',
  age: 10
}

(1) mapState

借助mapState生成成计算属性(computed中生成),从state中读取数据

import { mapState} from 'vuex'
computed: {
//原始获取state数据的方法
mingzi () {
    return this.$store.state.name
},
nianling () {
    return this.$store.state.age
},
// mapState对象写法。(生成计算属性的名称为he,值是state里的sum属性中读取的数据)
 ...mapState({ he: 'sum', mingzi: 'name', nianling: 'age' }),
// mapState数组写法,生成的计算属性:sum、name、age
...mapState(['sum', 'name', 'age']),  
}
// Count.vue组件
// 应用mapState对象写法生成的计算属性
<h3>学生姓名为:{{ mingzi }},年龄为:{{ nianling }}</h3>
// 数组写法
<h3>学生姓名为:{{ name }},年龄为:{{ age }}</h3>

在这里插入图片描述

(2) mapGetters

借助mapGetters生成计算属性,从getters中读取数据

import { mapGetters } from 'vuex'
//原始写法:
 bigSum () {
    return this.$store.getters.bigSum
 },
// 数组写法
...mapGetters(['bigSum']),
// 对象写法
...mapGetters({ bigSum: 'bigSum' })

(3) mapActions

借助mapActions生成对应的方法,方法中会调用dispatch去联系actions

import { mapActions } from 'vuex'
// 原始方法
incrementWait () {
    this.$store.dispatch('addWait', this.n)
}
 // 对象写法
...mapActions({ incrementOdd: 'addOdd', incrementWait: 'addWait' }),
// 数组写法
...mapActions(['addOdd', 'addWait']),

采用map形式时,组件中调用方法:

//mapActions采用对象写法,注意此处传参
<button @click="incrementOdd(n)">当前和为奇数再加</button>
//mapActions采用数组写法
<button @click="addOdd(n)">当前和为奇数再加</button>

(4) mapMutations

借助mapMutations生成对应的方法,方法中会调用commit去联系mutations

import { mapMutations, } from 'vuex'
// 原始写法
decrement () {
    this.$store.commit('MINUS', this.n)
},
// 对象写法
...mapMutations({ increment: 'ADD', decrement: 'MINUS' }),
// 数组写法
...mapMutations(['ADD','MINUS']),

总结

  1. mapState和mapGetters优化了计算属性;mapMutations和mapActions优化了方法。

  2. map方法采用对象写法{key:value}时:key为组件中使用该属性/方法时的名称,value为vuex里对应的属性名/方法名

  3. 采用数组写法时:组件中该属性名/方法名与vuex里的属性名/方法名一致

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

6. 模块化+命名空间namespace

根据功能,将相关的配置分类,让代码更好维护,让多种数据分类更加明确。

6.1 模块化

比如现在有Count和Dog两个vue组件。两个组件都往vuex中存放了一些数据。修改文件

// store.js
const countOptions ={
  namespaced: true, // 开启命名空间
  actions: {...},
  mutations: { ...},
  state: {...},
  getters: {...}
},
const dogOptions ={
  namespaced: true, // 开启命名空间
  actions: {...},
  mutations: { ...},
  state: {...},
  getters: {...}
},
export default new Vuex.Store({
 modules: {
  countAbout: countOptions,
  dogAbout: dogOptions
  // keyvalue名称一致时,也可以用简写形式
  // countOptions,
  // dogOptions
 }

或者将countOptions与dogOptions分别放入两个js文件

// count.js
export default {
  namespaced: true, // 开启命名空间
  actions: {...},
  mutations: { ...},
  state: {...},
  getters: {...}
}
// dog.js
import axios from 'axios'
import { nanoid } from 'nanoid'         
export default {
  namespaced: true, // 开启命名空间
  actions: {
   addDogServe (context) {//Actions部分向外发送请求
     axios.get('https://api.xygeng.cn/one').then(
       response => {
         context.commit('AddDogs', { id: nanoid(), name: response.data.data.tag })},
       error => {
         alert(error.message)}
    )}
 },
  mutations: { ...},
  state: {...},
  getters: {...}
}
// store.js
import countOptions from './count'
import dogOptions from './dog'
export default new Vuex.Store({
modules: {
    countAbout: countOptions,
    dogAbout: dogOptions
}

6.2 模块化后读取数据

由于将数据分类到不同的模块里,因此需要给模块开启命名空间namespace,才能确保读取到该模块的数据。

1、采用map方法读取数据,调用方法

// mapState方式读取不同模块的数据
...mapState('countAbout', { he: 'sum', mingzi: 'name', nianling: 'age', dogs: 'dogs' }),
...mapState('dogAbout', ['dogs']),
    
// mapGetters方式读取不同模块的数据
...mapGetters('countAbout', ['bigSum']),

// mapActions方式
...mapActions('countAbout', { incrementOdd: 'addOdd', incrementWait: 'addWait' }),
// mapMutations方式    
 ...mapMutations('countAbout', { increment: 'ADD', decrement: 'MINUS' }),

2、不采用map方法,自己读取state、getters数据,调用commit、dispatch方法

computed(){
 dogs () {
 	return this.$store.state.dogAbout.dogs
 },
 firstDog(){
    return this.$store.getters['dogAbout/getFirstDog']
 }
},
methods(){
 addDog () {
    this.$store.commit('dogAbout/AddDogs', { id: nanoid(), name: this.dogName })
 },
 addDogRandom () {
    this.$store.dispatch('dogAbout/addDogServe')
 }
}

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值