状态管理(vuex)
一、什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库;Vuex可以帮助我们管理共享状态
状态自管理应用包含:
- 状态,驱动应用的数据源;
- 视图,以声明方式将状态映射到视图;
- 操作,响应在视图上的用户输入导致的状态变化。

二、Vuex的作用
Vuex有五个核心概念:State、Getter、Mutation、Action、Module
state:
- 用于存放组件间通信的属性值
- state中的属性不能直接修改,需要在mutations中修改
//格式
// state.js
export default{
state:{
name:"张三"
}
}
// ./store/index.js
import state from './state'
export default new Vuex.Store({
state
})
//取值(在页面中)
<template>
<div>
{{$store.state.name}} <!-- 用法1 -->
<he />
{{name}} <!-- 用法三的使用 -->
</div>
</template>
<script>
import { mapState } from "vuex"
export default{
mounted(){
console.log(this.$store.state.name) //用法二
}
computed:{
...mapState(['name']) //用法三(相当于把属性放在this上)
}
}
</script>
getters:
- 是一些函数,类似于组件中的计算属性
- 可以处理一些通用且复杂的逻辑,实际使用较少
//格式
// getters.js
export default{
setName(state){
return '我是' + state.name
}
}
// ./store/index.js
import state from './state'
import getters from './getters'
export default new Vuex.Store({
state,
getters
})
//取值(在页面中)
<template>
<div>
{{setName}}
</div>
</template>
<script>
import { mapGetters } from "vuex"
export default{
computed:{
...mapGetters(['setName'])
}
}
</script>
mutations:
- 存放函数,可用于修改state中的属性值
- 只接受两个参数:state和options
- 原则上mutations必须都是同步函数
- 在组件中使用
this.$store.commit('xxx')
提交 mutation,或者使用mapMutations
辅助函数将组件中的 methods 映射为store.commit
调用 - 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式
//格式
// mutations.js
export default{
btn(){
alert(1)
},
setName(state,name){
state.userName = name
}
}
// state.js
export default{
list:[
{name:'张三'},
{name:'李四'},
{name:'王五'},
],
userName:''
}
// ./store/index.js
import state from './state'
import getters from './getters'
import mutations from './mutations'
export default new Vuex.Store({
state, //数据
getters, //计算属性
mutations //方法
})
//取值(在页面中)
<template>
<div>
<button @click="btn">按钮1</button> <!-- 用法1 -->
<button @click="run">按钮2</button> <!-- 用法2 -->
<ul>
<li v-for='(item,index) in list' :key='index' @click='showName(item.name)'>
</li> <!-- 用法3 -->
</ul>
<hr />
<h1>{{userName}}</h1>
</div>
</template>
<script>
import { mapMutations,mapState } from "vuex"
export default{
computed:{
...mapState(['list','userName'])
},
methods:{
...mapMutations(['btn','srtName']) //用法1
run(){
this.btn() //用法2
},
showName(name){
//this.userName = name ===>报错,不能直接修改state中的userName
this.setName(name) //用法3 传值
this.$store.commit("setName",name) //用commit提交mutation
}
}
}
</script>
如果想在每个页面都要使用到同一个函数可以执行commit提交mutation
//在main.js中
import store from './store'
store.commit("btn")
使用常量替代 mutation 事件类型,这样可以对mutation的函数作用更加一目了然
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// mutations.js
export default{
[SOME_MUTATION](){
alert('我被触发了');
}
}
// 在页面中
<template>
<div>
<button @click="SOME_MUTATION">按钮</button>
</div>
</template>
<script>
import { mapMutations } from "vuex"
export default{
methods:{
...mapMutations(['SOME_MUTATION'])
}
}
</script>
Action
- 提交的是mutation,而不是直接变更状态
- 可以包含任意异步操作
//mutations.js
import {ADD,RE,SUM} from './mutation-type' //在mutation-type中取函数类型
export default {
[ADD](state,obj){ //obj为action使用commit传过来的值
alert(obj)
state.count++
this.commit('SUM')
},
[RE](state){
state.count--
this.commit('SUM')
},
[SUM](state){
state.sum = state.count*10
}
}
//actions.js
export default{
addFn({commit},obj){
commit('ADD',obj) //使用mutation的ADD函数并传值obj
}
}
<template>
<div class="home">
<h1>{{count}}</h1>
<button @click="btn">按钮</button>
</div>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
name: 'Home',
computed:{
...mapState(['count'])
},
methods:{
...mapActions(['addFn']), //action
btn(){
this.addFn(1) //使用action中的addFn函数并传值1
}
}
}
</script>
mutation是同步函数而action是异步:
官网中这样说
代码解释:
//页面
<template>
<div class="home">
<h1>{{count}}</h1>
<button @click="run">mutations按钮(点击+10)</button>
</div>
</template>
<script>
import {mapState,mapMutations} from 'vuex'
export default {
name: 'Home',
computed:{
...mapState(['count'])
},
methods:{
...mapMutations(['setCount']),
run(){
this.setCount({
num:10
})
}
}
}
</script>
同步情况下:
//mutation.js
export default {
setCount(state,obj){
state.count += obj.num
}
}
结果如下:
数据都是对的上的,没有问题!
异步情况下:
//mutation.js
export default {
setCount(state,obj){
setTimeout(() => {
state.count += obj.num
}, 1000);
}
}
结果如下:
调式的数据对不上,出现异常了!
那么试试action中异步提交mutation:
//页面
<template>
<div class="home">
<h1>{{count}}</h1>
<button @click="run">mutations按钮(点击+10)</button>
</div>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
name: 'Home',
computed:{
...mapState(['count'])
},
methods:{
...mapActions(['setCountFn']),
run(){
this.setCountFn({
num:10
})
}
}
}
</script>
//mutation.js
export default {
setCount(state,obj){
state.count += obj.num
}
}
//actions.js
export default{
setCountFn({commit},obj){
setTimeout(() => {
commit("setCount",obj)
}, 1000);
}
}
结果如下:
数据对上了,正常!
这就是为什么mutation中混合异步调用会使你的程序很难调试,这就是mutation必须使用同步函数的原因!
Module
为了将模块更加细致分类,每个模块写一个仓库,都包含了state、mutation、getter、action
例如:
/* ./modules/list.js */
export default {
state:{},
getter:{},
mutation:{},
action:{}
}
/* ./store/index.js */
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import list from './modules/list.js'
export default new Vuex.Store({
modules:{
list,
...
}
})
// 页面中使用
computed:{
...mapState({
属性名:state=>state.list.属性名
})
}
总结:
Vuex是为了方便而使用的,当你觉得使用会让你方便的时候才使用。
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex