Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。
1 状态管理模式
1.1 单向数据流
1.2 多组件共享时【多组件间的数据通讯:切换-生命周期的销毁】
-
多个视图依赖于一个状态
-
来自不同视图的行为需要变更同一个状态
1.3 通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性
2 store仓库
每个 vuex 应用的核心就是
store
。store
基本上就是一个容器,它包含着应用中大部分的状态(state)
2.1 vuex 和单纯的全局对象有以下两点不同:
-
vuex 的状态存储是响应式的。
-
不能直接改变 store 中的状态。
2.2 实例化一个 store -- 一个应用只运行有一个 store
- 引入 vuex
import Vue from 'vue';
import Vuex from 'vuex';
- 使用(安装)vuex
Vue.use(Vuex);
- 实例化 store
const store = new Vuex.Store({
state: {
goodsList: [{
id: '',
name: '',
price: '',
qty: ''
}]
},
getters: {
totalPrice(state) {
return state.goodsList.reduce((prev, item)=> prev+item.price*item.qty, 0)
}
},
mutations: {
removeFromCart(state, id) {
state.goodsList=state.goodsList.filter(item=>item.id!=id)
},
clearCart(state) {
state.goodsList=[]
},
addCart(state, goods) {
state.goodsList.unshift(goods)
},
changeQty(state, payload) {
state.goodsList.forEach(item=>{
if(item.id == payload.id) {
item.qty = payload.qty
}
})
}
},
actions: {
async changeQtyAsync(context, {id, qty}) {
let {data} = await
if(qty > data) qty = data
context.commit('changeQty', {id, qty})
}
}
})
- 把 store 注入到 vue 实例
new Vue({
el: '#app',
router,
store
});
- 在组件中使用数据
this.$store
3 Store 存储空间--仓库
3.1 store
-
state
-- 真正存放数据的位置 -- 类似于组件中的data
-
getter
-- 类似于组件中的computed
-
mutations
-- 修改state
的方法 -- 类似于组件中的methods
-
actions
-- 异步修改state
的方法 -- 不能直接修改
3.2 state
通过在根实例注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能够通过
this.$store
访问到。
1. 操作 state
-
获取 state:
this.$store.state.xxx
-
修改 state:
this.$store.commit(mutation)
2. mapState
辅助函数【映射到 computed 】
当一个组件需要获得多个状态时,将这些状态都声明为计算属性会有些复杂和冗余。
- 使用
mapState
辅助函数自动生成计算属性。
import {mapState} from 'vuex'
export default {
computed: mapState({
count: state => state.count,
countAlias: 'count', // 等同于 state => state.count
countPlusLocalState(state) {
return state.count+this.localCount
}
})
}
- 当映射的计算属性的名称与 state 的子节点的名称相同时,可以给
mapState
传一个字符串数组。
computed: mapState(['count']) // this.count=store.state.count
3. 对象展开运算符
- 通过对象展开运算符,可以将
mapState
函数返回的对象与局部计算属性混合使用。
computed: {
localComputed() { },
...mapState({})
}
4. 组件仍然保有局部状态
3.3 Getter
vuex 允许我们在 store 中定义
getter
。就像计算属性一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖发生了改变才会被重新计算。
- Getter 接收
state
作为其第一个参数。
const store = new Vuex.Store({
state: {
todos: [
{id: 1, text: '...', done: true},
{id: 2, text: '...', done: false}
]
},
getters: {
doneTodos: state=> {
return state.todos.filters(todo=>todo.done)
}
}
})
- Getter 也接受其他 getter 作为第二个参数。
getters: {
doneTodosCount: (state, getters) {
return getters.doneTodos.length
}
}
- getter 在通过属性访问时是作为 vue 的响应式系统的一部分缓存其中的。
1. mapGetters 辅助函数
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters(['doneTodosCount','anotherGetter'])
}
}
- 使用对象形式,给一个 getter 属性另取一个名字
...mapGetters({
doneCount: 'doneTodosCount'
// doneCount: this.$store.getter.doneTodosCount
})
3.4 Mutation
-
更改 vuex 中 store 的状态的唯一方法就是提交
mutation
。 -
vuex 中的
mutation
类似于事件:每个mutation
都有一个事件类型 type + 回调函数 handler。这个回调函数就是我们实际进行状态更改的地方,并且它会接收 state 作为第一个参数。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment(state) {
state.count++
}
}
})
// 调用mutation handler
this.$store.commit('increment')
1. 提交载荷
- 可以向
store.commit
传入额外的参数,即mutation
的载荷payload
。
mutations: {
increment(state,n) {
state.count += n
}
}
// 调用
this.$store.commit('increment', 10)
- 多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的
mutation
会更易读。
mutations: {
increment(state, payload) {
state.count += payload.amount
}
}
// 调用
this.$store.commit('increment', {amount: 10})
2. 对象风格的提交方式
- 提交
mutation
的另一种方式是直接使用包含 type 属性的对象。当使用对象风格的提交方式,整个对象都作为载荷传给mutation
函数,因此 handler 保持不变。
this.$store.commit({type:'increment', amount: 10})
3. Mutation 必须是同步函数
-
每一条
mutation
被记录,devtools
都需要捕捉到前一状态和后一状态的快照。 -
任何在回调函数中进行的状态的改变都是不可追踪的。
mutations: {
someMutation(state) {
api.callAsyncMethod(()=>{state.count++})
}
}
4. 在组件中提交 Mutation
-
可以在组件中使用
this.$store.commit('xxx')
提交mutation
。 -
可以使用
mapMutations
辅助函数将组件中的methods
映射为store.commit
调用。
import {mapMutation} from 'vuex'
export default {
methods: {
...mapMutations(['increment','incrementBy']),
...mapMutations({add: 'increment'}),
...mapMutations({
changeQty2: (commit,payload)=> {
commit('changeQty',payload)
}
})
}
}
3.5 Action
-
Action
类似于mutation
,不同在于:-
Action
提交的是mutation
,而不是直接变更状态。 -
Action
可以包含任何异步操作。
-
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrtement(context) {
context.commit('increment')
}
}
})
-
Action
函数接受一个与store
实例具有相同方法和属性的context
对象。 -
可以调用
context.commit
提交一个mutation
,或者通过context.state
和context.getters
来获取 state 和 getters 。
actions: {
increment({commit}) {
commit('increment')
}
}
1. 分发 Actions
Actions
通过store.dispatch
方法触发。
actions: {
incrementAsync({commit}) {
setTimeout(()=> commit('increment'), 1000)
}
}
this.$store.dispatch('incrementAsync')
Actions
同样支持的载荷方式和对象方式进行发布。
this.$store.dispatch('incrementAsync', {amount: 10})
this.$store.dispatch({type: 'incrementAsync',amount: 10})
2. 在组件中分发 Action
-
在组件中使用
this.$store.dispatch('xxx')
分发action
。 -
可使用
mapActions
辅助函数将组件的methods
映射为store.dispatch
调用。
export default {
methods: {
...mapActions(['increment','incrementBy']),
...mapActions({add:'increment'}),
...mapActions({changeQtyAsync(dispatch, id, qty){
dispatch('changeQtyAsync',{id, qty})
}
})
}
}
3. 组合 Action
-
store.dispatch
可以处理被触发的action
的处理函数返回的Promise
,并且store.dispatch
仍旧返回Promise
。 -
一个
store.dispatch
在不同模块中可以触发多个action
函数。在这种情况下,只有当所有函数触发完成后,返回的Promise
才会执行。
actions: {
actionA({commit}) {
return new Promise((res, rej)=>{
setTimeout(()=>{
commit('someMutation')
res()
}, 1000)
})
},
actionB({dispatch, commit}) {
return dispatch('actionA').then(()=>{
commit('someOtherMutation')
})
}
}
this.$store.dispatch('actionA').then(()=>{})
actions: {
async actionA({commit}) {
commit('gotData', await getData())
},
async actionB({dispatch, commit}) {
await dispatch('actionA')
commit('gotOtherData', await getOtherData())
}
}
应用
// cart.js
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
data(){return {}},
computed: {
// goodsList() {
// return this.$store.state.goodsList
// },
// totalPrice() {
// return this.$store.getters.totalPrice
// },
...mapState({
goodsList(state) {
return state.goodsList
}
}),
...mapGetters(['totalPrice'])
},
methods: {
// removeItem(id) {
// this.$store.commit('removeFromCart', id)
// },
// clearCart() {
// this.$store.commit('clearCart')
// },
// changeQty(id, qty) {
// this.$store.dispatch('changeQtyAsync', {id, qty})
// }
...mapMutations({
removeItem: 'removeFromCart',
changeQty:(commit, payload)=>{
commit('changeQty', payload)
}
}),
...mapMutations(['clearCart']),
...mapActions({
changeQtyAsync(dispatch, id, qty) {
dispatch('changeQtyAsync', {id, qty})
}
})
}
}