共计 4310 个字符,预计需要花费 11 分钟才能阅读完成。
Vuex 是什么
官方文档说道:Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
什么是状态管理模式
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
当我们的应用遇到 多个组件共享状态 时,单向数据流的简洁性很容易被破坏,这时候我们就需要一个全局的状态管理来让我们的代码结构化
const store = new Vuex.Store({ | |
state: {count: 0}, | |
mutations: {increment (state) {state.count++} | |
} | |
}) |
store.commit('increment') | |
console.log(store.state.count) // -> 1 |
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
在 Vue 组件中获取状态
// 创建一个 Counter 组件 | |
const Counter = {template: `<div>{{ count}}</div>`, | |
computed: {count () {return store.state.count //this.$store.state.count} | |
} | |
}} |
从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
我们仍可以使用 mapState 函数帮我们生成计算属性 mapState
函数返回的是一个对象
import {mapState} from 'vuex' | |
export default { | |
// ... | |
computed: mapState({ | |
// 箭头函数可使代码更简练 | |
count: state => state.count, | |
// 传字符串参数 'count' 等同于 `state => state.count` | |
countAlias: 'count', | |
// 为了能够使用 `this` 获取局部状态,必须使用常规函数 | |
countPlusLocalState (state) {return state.count + this.localCount} | |
}) | |
} |
getter
import Vue from 'vue' | |
import Vuex from 'vuex' | |
Vue.use(Vuex) | |
let state = {count: 0} | |
let getters = { | |
total: state => {return '$' + state.count;} | |
} | |
let mutations = {increment(_state, n){_state.count += n || 1;}, | |
decrement(){state.count -= 1;} | |
} | |
const store = new Vuex.Store({ | |
state, | |
getters, | |
mutations | |
}) | |
export default store |
getter 通过属性访问
<h3>count-{{$store.getters.total}}</h3>
getter 通过方法访问
如果上面的方法想改变货币符号,那就要通过传参方式来解决
let getters = {total: (state) => (symbol) => {return (symbol || '$') + state.count; | |
} | |
} | |
<h3>count-{{$store.getters.total('¥')}}</h3> |
mapGetters 辅助函数
<template> | |
<div> | |
<h3>count-{{total('¥')}}</h3> | |
</div> | |
</template> | |
<script> | |
import common from '../../common/common.js' | |
import {mapState, mapMutations, mapGetters} from 'vuex'; | |
export default { | |
computed: { | |
...mapGetters(['total']) | |
} | |
} | |
</script> |
mapGetters 辅助函数之别名
<template> | |
<div> | |
<h3>count-{{amount('¥')}}</h3> | |
</div> | |
</template> | |
<script> | |
import common from '../../common/common.js' | |
import {mapState, mapMutations, mapGetters} from 'vuex'; | |
export default { | |
computed: { | |
...mapGetters({amount: 'total'}) | |
} | |
} | |
</script> |
Mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件,每个 mutation 的方法都会有一个 state 的参数在 commit 的时候当回调形参传过来,该形参就是 store.state
let mutations = {increment(_state){_state.count += 1;}, | |
} |
Mutation 传参数——提交载荷(Payload)
可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):
let mutations = {increment(_state){_state.count += 1;}, | |
} | |
this.$store.commit('increment', 10); |
大多数情况下要传多个参数,但是 mutation 的方法最多只有两个参数,一个是 state,另一个是 payload,所以参数可以用对象的方式去传。
Mutation 触发之 mapMutations 篇
<input type="button" value="increment" @click="increment(10)"/> | |
<script type="text/javascript"> | |
import {mapMutations} from 'vuex'; | |
methods: mapMutations(['increment']) | |
</script> |
Mutation 触发之 mapMutations 别名篇
<input type="button" value="increment" @click="add(10)"/> | |
<script type="text/javascript"> | |
import {mapMutations} from 'vuex'; | |
methods: mapMutations({add: 'increment'}) | |
</script> |
Mutation 触发之对象展开运算符篇
<input type="button" value="increment" @click="increment(10)"/> | |
<script type="text/javascript"> | |
import {mapMutations} from 'vuex'; | |
methods: {...mapMutations(['increment']), | |
} | |
</script> |
Mutation 触发之对象展开运算符别名篇
<input type="button" value="increment" @click="add(10)"/> | |
<script type="text/javascript"> | |
import {mapMutations} from 'vuex'; | |
methods: { | |
...mapMutations({add: 'increment'}), | |
} | |
</script> |
Action
先引用官方文档的说法
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
实现上是没问题,action 调用 mutation,但关于异步要放到 action 的说法,个人观点是没有这个必要,在 mutation 的小结中有说到过,mutation 只做同步也不是制性的
在使用 Action 前先与 Mutation 做个小结
- action 并不是必须的,项目中完全可以不需要 action
- 异步操作可放 mutation 和 action,只要开发时方便,都没有影响
- 关于官方说 action 异步,mutation 同步的说法只是为了能用 devtools 追踪状态变化。
- action 中的方法和 mutation 一样,最多只有两个形参,第一个为 context,可以理解为 store,第二个为手动传的参数
- action 用 commit() 来触发 mutation
- view 层通过 store.dispath 来分发 action
简单使用
在 action
store.js
import Vue from 'vue' | |
import Vuex from 'vuex' | |
Vue.use(Vuex) | |
let state = {count: 0} | |
let getters = {total: (state) => (symbol) => {return (symbol || '$') + state.count; | |
} | |
} | |
let mutations = {increment(_state, n){console.log(arguments) | |
_state.count += n || 1; | |
}, | |
decrement(){state.count -= 1;} | |
} | |
let actions = {increment(context, n){context.commit('increment', n) | |
} | |
} | |
const store = new Vuex.Store({ | |
state, | |
getters, | |
mutations, | |
actions | |
}) | |
export default store |
分发 action
<input type="button" value="increment" @click="$store.dispatch('increment', 5)"/>
mapActions
和 mutation 的使用方法基本一样
methods: {...mapActions(['increment']), | |
...mapActions({add: 'increment'}) | |
} |
Module
const moduleA = {state: { ...}, | |
mutations: {...}, | |
actions: {...}, | |
getters: {...} | |
} | |
const moduleB = {state: { ...}, | |
mutations: {...}, | |
actions: {...} | |
} | |
const store = new Vuex.Store({ | |
modules: { | |
a: moduleA, | |
b: moduleB | |
} | |
}) | |
store.state.a // -> moduleA 的状态 | |
store.state.b // -> moduleB 的状态 |