Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
组件Vue Component通过dispatch来调用actions提供的方法
而actions除了可以和api打交道外,还可以通过commit来调mutations提供的方法
最后mutaions将数据保存到state中
当然,Vue Components还以通过getters提供的方法获取state中的数据
Vuex 和普通全局对象有以下两点不同:
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
假设有这样一个场景:我们有一个父组件,同时包含两个子组件。父组件可以很容易的通过使用 props
属性来向子组件传递数据。
但是问题来了,当我们的两个子组件如何和对方互相通信的? 或者子组件如何传递数据给他父组件的?在我们的项目很小的时候,这个两个问题都不会太难,因为我们可以通过事件派发和监听来完成父组件和子组件的通信。
随着我们项目的增长:
保持对所有的事件追踪将变得很困难。到底哪个事件是哪个组件派发的,哪个组件该监听哪个事件?
项目逻辑分散在各个组件当中,很容易导致逻辑的混乱,不利于我们项目的维护。
父组件将变得和子组件耦合越来越严重,因为它需要明确的派发和监听子组件的某些事件。
注意:虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
// 官方学习地址
https://vuex.vuejs.org/zh/guide/strict.html
// 比较棒的讲解
https://juejin.im/entry/59191b6b0ce4630069f6a3ad
// example git地址
https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart
四大核心模块:
state: 状态,state其实是数据状态管理对象,在这里你可以初始化一些你想要的数据。
getter: 获得者,getter是对state的数据对象的读取,getters从表面是获得的意思,可以把他看作在获取数据之前进行的一种再编辑,相当于对数据的一个过滤和加工。getters就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
Actions:行动,Actions里面我们可以定义我们想执行异步的方法,在这里它并不会立即去执行,而是在页面中去dispatch这个方法,提交mutations,而不是直接去改变状态,在页面中有两种方式去做派发,第一种 this.$store.dispatch('xxx')
第二种 可以使用mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(Demo中有体现)
mutations: 突变,mutations里面可写很多的改变状态的方法,也就是像翻译一样,可以改变state里面的数据,试讲state的里面数据发生改变的唯一方式。
...
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
mapState
辅助函数获取多个状态时候,使用 mapState
辅助函数帮助我们生成计算属性。
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState
传一个字符串数组
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
mapGetters
辅助函数Getter 接受 state 作为其第一个参数:
const getters={
counte:(state)=>{
return state.count+=100
}
}
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit
提交一个 mutation。
mutation 必须同步执行,Action 就不受约束!我们可以在 action 内部执行异步操作。
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const state = {
count: 3
}
const mutations = {
add(state, n) {
state.count += n ;
},
reduce(state) {
state.count--;
}
}
const getters = {
count: (state) => {
return (state.count+ 100)
}
}
const actions = {
addAction(context) {
context.commit('add', 5);
// 异步输出
setTimeout(() => {
context.commit('reduce')
}, 1000)
},
reduceAction({
commit
}) {
commit('reduce');
},
}
let store = new Vuex.Store({
state,
mutations,
getters,
actions
});
export default store;
// 使用vue文件使用
123123
{{msg}}
{{$store.state.count}}:{{count}}
// cart.js
import shop from '../../api/shop'
// initial state
// shape: [{ id, quantity }]
const state = {
items: [],
checkoutStatus: null
}
// getters
const getters = {
cartProducts: (state, getters, rootState) => {
return state.items.map(({ id, quantity }) => {
const product = rootState.products.all.find(product => product.id === id)
return {
title: product.title,
price: product.price,
quantity
}
})
},
cartTotalPrice: (state, getters) => {
return getters.cartProducts.reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
}
// actions
const actions = {
checkout ({ commit, state }, products) {
const savedCartItems = [...state.items]
commit('setCheckoutStatus', null)
// empty cart
commit('setCartItems', { items: [] })
shop.buyProducts(
products,
() => commit('setCheckoutStatus', 'successful'),
() => {
commit('setCheckoutStatus', 'failed')
// rollback to the cart saved before sending the request
commit('setCartItems', { items: savedCartItems })
}
)
},
addProductToCart ({ state, commit }, product) {
commit('setCheckoutStatus', null)
if (product.inventory > 0) {
const cartItem = state.items.find(item => item.id === product.id)
if (!cartItem) {
commit('pushProductToCart', { id: product.id })
} else {
commit('incrementItemQuantity', cartItem)
}
// remove 1 item from stock
commit('products/decrementProductInventory', { id: product.id }, { root: true })
}
}
}
// mutations
const mutations = {
pushProductToCart (state, { id }) {
state.items.push({
id,
quantity: 1
})
},
incrementItemQuantity (state, { id }) {
const cartItem = state.items.find(item => item.id === id)
cartItem.quantity++
},
setCartItems (state, { items }) {
state.items = items
},
setCheckoutStatus (state, status) {
state.checkoutStatus = status
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
// ShoppingCart.vue
Your Cart
Please add some products to cart.
-
{{ product.title }} - {{ product.price | currency }} x {{ product.quantity }}
Total: {{ total | currency }}
Checkout {{ checkoutStatus }}.