vuex
专门在vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
使用场景:
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
搭建Vuex环境
- 创建文件:
src/store/index.js
```javascript // 用于创建vuex中最核心的store
// 引入vuex import Vue from ‘vue’ 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, // actions:actions mutations, state });
2. 在`main.js`中创建vm时传入`store`配置项
```javascript
import Vue from 'vue'
import App from './App.vue'
// 引入store,因为store写的是index.js,所以此处不需要写完整的./store/index.js路径
import store from './store'
new Vue({
render: h => h(App),
store, // 使用vuex插件之后,就可以使用store配置项了
}).$mount('#app')
此时,Vue实例对象vm和所有的组件实例对象vc都可以通过 this.$store
获取到store对象,通过store的dispatch('xxx', value)
调用actions
中配置的方法,通过store的commit('xxx',value)
调用mutations中配置的方法,通过state
获取到state中配置的具体变量。
Vuex的一般流程为:
- 组件中通过
this.$store.dispatch('xxx', value)
调用 actions中配置的方法 - actions的方法中,通过
context.commit('xxx', value)
调用 mutations中配置的方法 - mutations的方法中,对
state
中的变量进行修改操作
actions可以理解为饭店的服务员:组件调用dispatch
告诉actions服务员点菜。服务员可以进行一些判断逻辑,比如客户是否有忌口、厨房是否还有原材料等(发送ajax去菜市场买菜也在这里处理)
mutations可以理解为饭点的厨师:actions中调用commit
通知厨师做具体的菜。厨师对state
中具体的原材料变量进行处理。
如果actions中没有什么判断逻辑,只是单纯的调用了
commit
通知厨师,那么就可以组件中通过commit
直接通知厨师做菜。虽然actions中也可以对
state
进行操作做菜,但是不推荐这么做。因为页面控制台的Vuex监控插件只会监控mutations对state的改变,不会监控actions。
示例
在index.js文件编写actions、mutations、state:
// .......
// 准备actions,用于响应组件中的动作
const actions = {
// context是一个mini版的store,拥有store的dispatch、commit等相关方法
jia(context, value) {
context.commit('JIA', value);
},
jian(context, value) {
context.commit('JIAN', value);
},
jiaOdd(context, value) {
if(context.state.sum % 2) {
context.commit('JIA', value)
}
},
jiaWait(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 500);
},
demo1(context, value) {
// actions中的方法,可以通过dispatch继续调用其他方法。
context.dispatch('demo2', value)
},
demo2(context,value) {
context.commit('JIA', value)
}
}
// 准备mutations,用于操作数据(state)
const mutations = {
// mutations内部的方法名一般用大写
JIA(state, value) {
state.sum += value
},
JIAN(state, value) {
state.sum -= value
}
}
// 准备state,用于存储数据
const state = {
sum:0
}
// .........
组件中调用相关方法:
<template>
<div>
<!-- 读取Vuex中的数据 -->
<h2>当前求和为:{{$store.state.sum}}</h2>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1, // 用户选择的数字
}
},
methods: {
increment() {
// this.$store.dispatch('jia', this.n);
// actions中没有其他逻辑,只是调用commit。那么就可以在组件中通过commit直接调用mutations中的方法
this.$store.commit('JIA', this.n)
},
decrement() {
// this.$store.dispatch('jian', this.n);
this.$store.commit('JIAN', this.n)
},
incrementOdd() {
// 通过dispatch调用actions中的方法
this.$store.dispatch('jiaOdd', this.n)
},
incrementWait() {
this.$store.dispatch('jiaWait', this.n)
}
},
}
</script>
getters
类似于计算属性,位于store
中。当state中的数据需要加工后再使用时,可以使用getters加工。
这个属性是非必须的,可以根据需要添加。
示例:
在index.js中配置getters
// 定义getters
const getters = {
bigSum(state) { // 可以接收到state
return state.sum * 10; // 需要有返回值
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters // 将getters配置到store中
});
在页面使用getters:
<template>
<div>
<h2>当前求和为:{{$store.state.sum}}</h2>
<!-- 通过$store.getters获取到getters配置的属性 -->
<h2>当前求和放大10倍为:{{$store.getters.bigSum}}</h2>
</div>
</template>
4个map方法的使用
mapState
可以用于帮我们映射state
中的数据为计算属性。
mapGetters
用于帮我们映射getters
中的数据为计算属性。
mapActions
用于帮我们生成与actions
对话的方法,即包含$store.dispatch(xxx)
的函数
mapMutations
用于帮我们生成与mutations
对话的方法,即包含$store.commit
的函数
如果要从state
中获取很多属性,直接使用计算属性的写法为:
<template>
<div>
<h2>求和:{{he}}</h2>
<h2>学校:{{xuexiao}}</h2>
<h2>学科:{{xueke}}</h2>
</div>
</template>
<script>
export default {
name:'Count',
computed: {
he() {
return this.$store.state.sum;
},
xuexiao() {
return this.$store.state.school;
},
xueke() {
return this.$store.state.subject;
}
}
}
</script>
每次都要写this.$store.state.
比较繁琐,可以使用mapState
进行简写:
<script>
// 引入mapState
import {mapState} from 'vuex'
export default {
name:'Count',
computed: {
// mapState是一个对象,需要使用解构语法对其属性进行解构
// 此时就相当于在计算属性中定义了he、xuexiao、xueke这些计算属性,冒号后面为映射到的state中的属性
...mapState({he:'sum', xuexiao:'school', xueke:'subject'})
}
}
</script>
特别的,如果计算属性和state
属性名相同,可以使用数组简写:
<script>
// 引入mapState
import {mapState} from 'vuex'
export default {
name:'Count',
computed: {
// ...mapState({sum:'sum', school:'school', subject:'subject'})
// 数组简写
...mapState(['sum', 'school', 'subject'])
}
}
</script>
同样的,如果要获取getters
中的属性,可以使用mapGetters
进行简写:
<script>
// 引入mapState、mapGetters
import {mapState,mapGetters} from 'vuex'
export default {
name:'Count',
computed: {
...mapState(['sum', 'school','subject']),
// bigSum() {
// return this.$store.getters.bigSum
// }
// 简写为
// ...mapGetters({bigSum:'bigSum'})
// 或者简写为
...mapGetters(['bigSum'])
}
}
</script>
如果要调用actions
中的dispatch
方法,直接在methods
中写法为:
<template>
<div>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1,
}
},
methods: {
incrementOdd() {
// 通过dispatch调用actions中的方法
this.$store.dispatch('jiaOdd', this.n)
},
incrementWait() {
this.$store.dispatch('jiaWait', this.n)
}
}
}
</script>
可以使用mapActions
简写$store.dispatch
:
<template>
<div>
<!-- 使用mapActions时,需要在调用方法时直接将参数传进去 -->
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1,
}
},
methods: {
...mapActions({incrementOdd:'jiaOdd', incrementWait:'jiaWait'}) // 对象写法
}
}
</script>
如果组件中方法名和actions中方法名同名,则在mapActions
中同样可以使用数组写法。
同样的,可以使用mapMutations
简写$store.commit
:
<template>
<div>
<!-- 使用mapMutations时,需要在调用方法时直接将参数传进去 -->
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
export default {
name:'Count',
data() {
return {
n:1,
}
},
methods: {
// mapMutations同样有对象写法、数组写法
...mapMutations({increment:'JIA', decrement:'JIAN'}),
}
}
</script>
mapActions和mapMutations使用时,若需要传递参数:需要在模板中绑定事件时传递好参数,否则参数是事件对象。
模块化和命名空间
目的:让代码更好维护,让多种数据分类更加明确。
示例:修改store的index.js
// 定义count组件用到的
const countOptiosn = {
namespaced: true,
actions: {
jia(context, value) {
// console.log('actions的jia调用了', context, value);
context.commit('JIA', value);
},
jian(context, value) {
context.commit('JIAN', value);
},
jiaOdd(context, value) {
if(context.state.sum % 2) {
context.commit('JIA', value)
}
},
jiaWait(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 500);
}
},
mutations: {
JIA(state, value) {
// console.log('mutations的JIA调用了', state, value)
state.sum += value
},
JIAN(state, value) {
state.sum -= value
}
},
state: {
sum:0,
school: '庞各庄小学',
subject: '数学'
},
getters: {
bigSum(state) {
return state.sum * 10;
}
}
}
// 定义person组件用到的
const personOptions = {
namespaced: true,
actions: {
addLiSi(context, value) {
if(value.name === '李四') {
context.commit('addUser', value);
} else {
alert('只能输入李四进行添加')
}
}
},
mutations: {
addUser(state, value) {
state.personList.unshift(value);
}
},
state: {
personList: [{id:'001', name:'张三'}]
},
getters: {
}
}
// 使用Vuex插件
Vue.use(Vuex)
// 创建并暴露store
export default new Vuex.Store({
// 使用modules进行暴露
modules: {
countOptions: countOptions,
personOptions: personOptions
}
});
在组件中使用map读取命名空间的数据:
export default {
name:'Count',
methods: {
// 调用countOptions命名空间的mutations
...mapMutations('countOptions',{increment:'JIA', decrement:'JIAN'}),
// 调用countOptions命名空间的actions
...mapActions('countOptions',{incrementOdd:'jiaOdd', incrementWait:'jiaWait'})
},
computed: {
// 读取countOptions命名空间的state
...mapState('countOptions',['sum', 'school','subject']),
// 读取countOptions命名空间的getters
...mapGetters('countOptions',{bigSum:'bigSum'})
}
}
在组件中直接读取命名空间的数据:
export default {
name: 'Person',
data() {
return {
name: ''
}
},
computed: {
personList() {
// 读取personOptions命名空间的state数据
return this.$store.state.personOptions.personList;
},
countSum() {
// 读取countOptions命名空间的getters数据
return this.$store.getters['countOptions/bigSum']
}
},
methods: {
addUser() {
// 调用personOptions命名空间的mutations方法
this.$store.commit('personOptions/addUser', {id:nanoid(), name: this.name});
this.name = '';
},
addLiSi() {
// 调用personOptions命名空间的actions方法
this.$store.dispatch('personOptions/addLiSi',{id:nanoid(), name: this.name});
this.name = '';
}
}
}