文章目录
- 一、Vuex
-
- 前言:求和案例
- [1. 搭建Vuex环境](#1. 搭建Vuex环境)
- [2. 基本使用](#2. 基本使用)
- [3. 常见疑惑](#3. 常见疑惑)
- [4. getters](#4. getters)
- [5. 四个map方法的使用](#5. 四个map方法的使用)
-
- [(1) mapState](#(1) mapState)
- [(2) mapGetters](#(2) mapGetters)
- [(3) mapActions](#(3) mapActions)
- [(4) mapMutations](#(4) mapMutations)
- [6. 模块化+命名空间namespace](#6. 模块化+命名空间namespace)
-
- [6.1 模块化](#6.1 模块化)
- [6.2 模块化后读取数据](#6.2 模块化后读取数据)
一、Vuex
Vuex是一个Vue插件,用来实现集中式状态(数据)管理。简单来说就是,当多个组件需要读写同一数据时。使用vuex来管理这个数据,会更方便多个组件读写。
原理图:
前言:求和案例
接下来都是结合这个求和案例来说明vuex的使用
Count组件
的点击事件分别为:
html
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
对应的监听事件为:
javascript
// 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
javascript
import Vuex from 'vuex'
Vue.use(Vuex)
有这两句代码之后,vue实例里就可以写store配置项。写了store配置项,所有的vc和vm都能够接触到$store
3.创建store文件:src/store/index.js
javascript
//引入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
})
- 在
main.js
中创建vm时传入store
配置项
javascript
//引入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
javascript
// 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中的数据
javascript
// Count.vue里监听事件
increment () {
this.$store.dispatch('add', this.n)
},
3.组件中读取vuex中的数据:
html
<!--(插值表达式中不需要写this)-->
<h1>当前求和为{{ $store.state.sum }}</h1>
4.如果没有其他逻辑,组件也可以越过actions直接给mutations发送消息
javascript
// 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文件中添加配置
javascript
...
const getters = {
bigSum (state) {
return state.sum * 10
}
}
...
export default new Vuex.Store({
...
getters
})
3.组件中读取getters里的数据 $store.getters.bigSum
5. 四个map方法的使用
假设在store/index.js
中:
javascript
// 准备state------用于存储数据
const state = {
sum: 0,
// 新增两个属性
name: 'tom',
age: 10
}
(1) mapState
借助mapState生成成计算属性(computed中生成),从state中读取数据
javascript
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']),
}
javascript
// Count.vue组件
// 应用mapState对象写法生成的计算属性
<h3>学生姓名为:{{ mingzi }},年龄为:{{ nianling }}</h3>
// 数组写法
<h3>学生姓名为:{{ name }},年龄为:{{ age }}</h3>
(2) mapGetters
借助mapGetters生成计算属性,从getters中读取数据
javascript
import { mapGetters } from 'vuex'
//原始写法:
bigSum () {
return this.$store.getters.bigSum
},
// 数组写法
...mapGetters(['bigSum']),
// 对象写法
...mapGetters({ bigSum: 'bigSum' })
(3) mapActions
借助mapActions生成对应的方法,方法中会调用dispatch
去联系actions
javascript
import { mapActions } from 'vuex'
// 原始方法
incrementWait () {
this.$store.dispatch('addWait', this.n)
}
// 对象写法
...mapActions({ incrementOdd: 'addOdd', incrementWait: 'addWait' }),
// 数组写法
...mapActions(['addOdd', 'addWait']),
采用map形式时,组件中调用方法:
html
//mapActions采用对象写法,注意此处传参
<button @click="incrementOdd(n)">当前和为奇数再加</button>
//mapActions采用数组写法
<button @click="addOdd(n)">当前和为奇数再加</button>
(4) mapMutations
借助mapMutations生成对应的方法,方法中会调用commit去联系mutations
javascript
import { mapMutations, } from 'vuex'
// 原始写法
decrement () {
this.$store.commit('MINUS', this.n)
},
// 对象写法
...mapMutations({ increment: 'ADD', decrement: 'MINUS' }),
// 数组写法
...mapMutations(['ADD','MINUS']),
总结:
-
mapState和mapGetters优化了计算属性;mapMutations和mapActions优化了方法。
-
map方法采用对象写法{key:value}时:key为组件中使用该属性/方法时的名称,value为vuex里对应的属性名/方法名
-
采用数组写法时:组件中该属性名/方法名与vuex里的属性名/方法名一致
**注:**mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数默认是事件对象。
6. 模块化+命名空间namespace
根据功能,将相关的配置分类,让代码更好维护,让多种数据分类更加明确。
6.1 模块化
比如现在有Count和Dog两个vue组件。两个组件都往vuex中存放了一些数据。修改文件
javascript
// 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文件
javascript
// 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方法读取数据,调用方法
javascript
// 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方法
javascript
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')
}
}