Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
一、安装
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中的数组
同一个actions中调用方法,使用store.dispatch方法 mutations
mutations中可以得到并修改state中的数据
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>
组件中读取数据:$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中提供了两种方法—mapState和mapGetters
组件方法中可能多次使用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)
}
可以利用mapMutations和mapActions方法进行简化
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结点下
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>
方法不传参但需要用到参数,会出现类似如下错误:
模块化
将一个业务对象的业务封装到一起,另外创建一个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所识别。
否则出现类似如下错误:
利用两个业务对象举例开启命名空间后组件如何读取/使用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.分类名.变量名。