入门
简介
Vuex是专门为Vue应用程序开发的集中式状态(数据)管理插件,可以对Vue中的组件进行统一的数据管理。
搭建环境
vue2的工程使用vux3,vue3的工程需要使用vux4,版本需要对应,这里以安装vux3为例
在脚手架中执行:
npm i vuex@3
工作流程

- Action主要用于响应Vue Conponents的消息,或者从后端接收数据,并且可以将数据在Action中进行初步处理(主要为异步处理,如:添加定时器)
- Mutation主要用于处理数据,在Mutation处理的数据才能被Devtools调试工具监测
- State主要用于集中存储数据
工作流程:Vue Conponents发送消息(dispatch)给Action,或者Action通过Ajax等技术从后端异步获取数据 —> Actions提交(commit)到Mutation —> 数据在Mutation中进行处理后,修改(mutate)State中存储的数据 —> 监测到State数据发生变化,Vue重新渲染(render)页面
vue组件的中数据不要预处理时,也可以越过Action,直接commit到mutation中进行处理
关于store:store是Vuex的核心库,可以理解为一个容器,Action、Mutation、state由Store统一管理,在进行消息提交、数据操作时往往需要经过store,通过this.$store.dispatch、this.$store.commit等语句来调用api
上手
开发步骤
1. 配置store
在src目录下新建store目录,新建index.js文件
import Vue from 'vue'
import Vue from 'vue'
//引入并应用vux
import Vuex from 'vuex'
Vue.use(Vuex)
//创建action、mutations、state
const actions={....}
const mutations={....}
const state={.....}
//创建并暴露Store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
2. 引入store配置项
在main.js中引入store配置项
// 全写为import store from './store/index.js'
import store from './store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
案例
eg:简单求和差案例,4个按钮分别实现求和、求差、判断偶数后求和、定时器延时1s求和
- 直接求和、求差可以直接commit到Mutation中运算
- 判断当前值是否为偶数,延时求和需要dispatch到Action中预处理,再commit到Mutation中运算
- 需要多次处理的数据可以在Action中多次dispatch,处理完毕再commit
1. 创建组件
在components中创建Count.vue
<template>
<div>
<h2>求和案例</h2>
<h2>sum经过getters预处理后{{$store.getters.addTen}}</h2>
<h3>当前值为{{$store.state.sum}}</h3>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button @click="incrace">加</button>
<button @click="decrace">减</button>
<button @click="addOpp">偶数才加</button>
<button @click="addLate">延迟3s加</button>
</div>
</template>
<script>
export default {
name:'CountSum',
data(){
return {
num:1, //加数
}
},
methods:{
// 求和、求差直接commit
incrace(){
this.$store.commit('JIA',this.num)
},
decrace(){
this.$store.commit('JIAN',this.num)
},
//延时、判断奇偶dispatch到actions经过处理后再提交
addOpp(){
this.$store.dispatch('addOpp',this.num)
},
addLate(){
this.$store.dispatch('addLate',this.num)
}
}
}
</script>
2. 配置store
新建store目录并在该目录下新建index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions={
// actions里的方法名一般小写
//接收参数 (context,实参)
// context是一个对象,拥有store的部分属性,变量名可以为其他
addOpp(context,value){
if(!(value%2)){ //判断是否为偶数,为偶数则commit到mutations
context.commit('JIA',value)
}else{
console.log('非偶数,数未提交')
}
},
addLate(context,value){ //数据想要多道处理时,可以在actions内多次dispatch,无限套娃
setTimeout(()=>{
context.dispatch('addLate2',value);
console.log('第一次延迟处理')
},500)
},
addLate2(context,value){ //数据经过第二道处理后,再提交
setTimeout(()=>{
context.commit('JIA',value);
console.log('第二次延迟处理')
},500)
}
}
// mutations里的方法名一般大写,用于和actions里的区别
const mutations={
//接收参数(state,实参)
JIA(state,value){ //加
state.sum+=value
},
JIAN(state,value){ //减
state.sum-=value;
}
}
const state={ //存储数据
sum:0
}
const getters={ //读取数据前,想要预先进行处理使用getters
addTen(state){
return state.sum+10
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
3. 引入store配置项
在main.js中引入store配置
import Vue from 'vue'
import App from './App.vue'
import store from './store'// 全写为import store from './store/index.js'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
4. 引入Count组件
在App中使用Count组件
<template>
<div id="app">
<Count/>
</div>
</template>
<script>
import Count from'./components/Count.vue'
export default {
name:'App',
components:{
Count
}
}
</script>
getters
组件从state读取数据前,如果需要对数据进行预处理,可以在getter中进行。如:读取state中的num前进行处理(完整代码位于上一个案例)
1. 在store中添加getter
const actions={.....}
const mutations={....}
const state={.....}
const getter={
//读取state中的sum前,将值增大10
addTen(state){
return state.sum+10;
}
}
2. 读取数据
读取数据使用
$store.getters.addTen
辅助函数
当一个组件需要获取多个数据(状态)时,调用数据和api需要大量使用this.$store.state.number等语句,为了减小代码书写量,可以在计算属性中借助mapState、mapGetters、mapMutations、mapActions辅助函数简化代码
通过对象
当组件中的方法名、变量名与State、Mutations…中的变量名不同时,需要通过对象方式接收。并借助模板语法解析变量,以读取State中的数据为例:
<template>
<div>
<h2>求和案例</h2>
<h2>sum经过getters预处理后{{addTen}}</h2>
<h3>当前值为{{sum1}}</h3>
<h2>从state获取name为{{name1}},从state获取song为{{song1}}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
//当使用mapState等语法时,需要在写函数时传参(num)
<button @click="incrace(num)">加</button>
<button @click="decrace(num)">减</button>
<button @click="addOpp(num)">偶数才加</button>
<button @click="addLate(num)">延迟3s加</button>
</div>
</template>
<script>
//引入mapState、mapGetters、mapMutations、mapActions
import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
export default {
name:'CountSum',
data(){
return {
num:1,
}
},
computed:{
//模板语法
...mapState({sum1:'sum',name1:'name',song1:'song'}),
...mapGetters({addTen:'addTen'})
// 可以简写为数组形式...mapGetters(['addTen']),在对象中不能简写,会解析为addTen:addTen,值也解析为变量
},
methods:{
//原语句
// incrace(){this.$store.commit('JIA',this.num)},
// decrace(){this.$store.commit('JIAN',this.num)},
// 借助mapMutations生成对应方法,该方法会自动调用commit,数组写法在另一组件中
...mapMutations({incrace:'JIA',decrace:'JIAN'}),
//原语句
// addOpp(){this.$store.dispatch('addOpp',this.num)},
// addLate(){this.$store.dispatch('addLate',this.num)}
...mapActions({addOpp:'addOpp',addLate:'addLate'})
}
}
</script>
通过数组
当组件中的方法名、变量名与State、Mutations…中的变量名相同时,可以直接使用数组
<template>
<div>
<h2>不同的mapstate等写法</h2>
<h2>sum经过getters预处理后{{addTen}}</h2>
<h3>当前值为{{sum}}</h3>
<h2>从state获取name为{{name}},从state获取song为{{song}}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button @click="JIA(num)">加</button>
<button @click="JIAN(num)">减</button>
<button @click="addOpp(num)">偶数才加</button>
<button @click="addLate(num)">延迟3s加</button>
</div>
</template>
<script>
import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
export default {
name:'CountSum',
data(){
return {
num:1,
}
},
computed:{
...mapState(['sum','name','song']),
...mapGetters(['addTen'])
},
methods:{
//数组写法
...mapMutations(['JIA','JIAN']),
...mapActions(['addOpp','addLate'])
}
}
</script>
模块化与命名空间
当有多类数据需要vuex管理时,可以将他们的state、actions、mutation封装到多个js文件中,并为它们开启命名空间
方法
- 在store目录中创建多个store配置文件
- 将配置文件统一引入该目录的index.js中
- 将store配置引入main.js中
- 创建组件,需要注意辅助函数的用法,指向命名空间的方法
- 在app中引入组件
案例
eg: 在上个案例基础上加入添加成员的功能。现在有两个功能:求和求差、添加成员,因此store目录下需要两个store配置项。在组件中调用数据时,需要用到命名空间,从不同state中引用数据。
1. 配置store
在store目录新建 person.js文件
//添加人员模块
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const person = {
namespaced:true, //开启命名空间
actions:{
addPersonZhou(context,value){
if(value.name.indexOf('周')===0){
context.commit('ADD_PERSON',value)
}
else{
alert('只能添加姓周的名')
}
},
},
mutations:{
ADD_PERSON(state,personObj){
state.personList.unshift(personObj);
}
},
state:{
personList:[{id:'001',name:'张三'}]
},
getters:{
getFirstName(state){
return state.personList[0].name;
}
}
}
export default person
在store目录新建 count.js文件
//求和模块
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default {
namespaced:true, //开启命名空间
actions: {
addOpp(context,value){ //判断是否为偶数
if(!(value%2)){
context.commit('JIA',value)
}else{
console.log('非偶数,数未提交')
}
},
addLate(context,value){ //数据想要多道处理时,可以dispatch为actions内的其他方法,无限套娃
setTimeout(()=>{
context.dispatch('addLate2',value);
console.log('第一次延迟处理')
},500)
},
addLate2(context,value){ //数据经过第二道处理后,再提交
setTimeout(()=>{
context.commit('JIA',value);
console.log('第二次延迟处理')
},500)
}
},
mutations:{
JIA(state,value){ //加
state.sum+=value
},
JIAN(state,value){ //减
state.sum-=value;
},
},
state:{
sum:0,
name:'周杰伦',
song:'夜曲',
},
getters:{
addTen(state){
return state.sum+10
}
}
}
2. 在index.js中引入
在store目录下index.js中引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//引入两个配置项
import count from './count'
import person from './person'
// 创建并暴露store
export default new Vuex.Store({
//引入模块
modules:{
//全写count: count,
count,
person
}
})
3. 在main.js中引入store
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
4. 书写组件
创建count.vue组件
<template>
<div>
<h2>求和案例</h2>
<h2>sum经过getters预处理后{{addTen}}</h2>
<h3>当前值为{{sum}}</h3>
<h2>从state获取name为{{name}},从state获取song为{{song}}</h2>
<select v-model.number="num">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button @click="incrace(num)">加</button>
<button @click="decrace(num)">减</button>
<button @click="addOpp(num)">偶数才加</button>
<button @click="addLate(num)">延迟3s加</button>
<h1>组件2共享过来的人员名单</h1>
<ol class="person-list">
<li v-for="person in personList" :key="person.id">{{person.name}}</li>
</ol>
</div>
</template>
<script>
import {mapState,mapGetters, mapMutations,mapActions} from 'vuex'
export default {
name:'CountSum',
data(){
return {
num:1,
}
},
//使用mapState,mapGetters, mapMutations,mapActions时更简洁
computed:{
// 从count组件获取数据
...mapState('count',{sum:'sum',name:'name',song:'song'}),
// 从person组件获取数据
...mapState('person',['personList']),
...mapGetters('count',{addTen:'addTen'})
},
methods:{
...mapMutations('count',{incrace:'JIA',decrace:'JIAN'}),
...mapActions('count',{addOpp:'addOpp',addLate:'addLate'})
}
}
</script>
person.vue组件
<template>
<div>
<h2>组件2:添加成员</h2>
<input type="text" v-model="personName" placeholder="添加成员">
<button @click="add">添加</button>
<button @click="addZhou">只添加姓周的人</button>
<ul>
<li v-for="person in personList" :key="person.id">{{person.name}}</li>
</ul>
<h2>名单第一个人为{{firstPersonName}}</h2>
<h2>组件一共享的值</h2>
<h3>和为:{{sum}}</h3>
</div>
</template>
<script>
import { nanoid } from 'nanoid'
export default {
name:'CountSum',
data(){
return {
personName:''
}
},
//不使用mapState,mapGetters, mapMutations,mapActions时更新、获取数据的方法
computed:{
personList(){
return this.$store.state.person.personList
},
sum(){
return this.$store.state.count.sum
},
//通过getters获取时,想要指定组件名与路径
firstPersonName(){
return this.$store.getters['person/getFirstName']
}
},
methods:{
add(){
const personObj={id:nanoid(),name:this.personName};
//指定为person组件下的ADD_PERSON函数
this.$store.commit('person/ADD_PERSON',personObj);
this.personName='';
},
addZhou(){
const personObj={id:nanoid(),name:this.personName};
this.$store.dispatch('person/addPersonZhou',personObj);
this.personName='';
}
}
}
</script>
5. App.vue引入两个组件即可
<template>
<div id="app">
<Count/>
<Person/>
</div>
</template>
<script>
import Count from'./components/Count.vue'
import Person from'./components/Person.vue'
export default {
name:'App',
components:{
Count,
Person
}
}
</script>