介绍
Vuex 是⼀个专为 Vue.js 应⽤程序开发的状态管理模式。它采⽤集中式存储管理应⽤的所有组件的状态,并以相应的规则保证状态以⼀种可预测的⽅式发⽣变化。
安装Vuex
vue add vuex
复制代码
Store.js
import Vue from 'vue';
import Vuex from 'vuex';
//确保开头调用Vue.use(Vuex)
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//this.$store.state.count
count: 0
},
getters: {
evenOrOdd: (state) => {
//this.$store.getters.evenOrOdd
return state.count % 2 === 0 ? '偶数' : '奇数'
}
},
mutations: {
increment(state) {
//this.$store.commit('increment')
state.count++
},
decrement(state) {
//this.$store.commit('decrement')
state.count--
}
},
actions: {
increment({commit}) {
//this.$store.dispatch('increment')
//修改状态的唯一方式是提交mutation
commit('increment');
},
decrement({commit}) {
//this.$store.dispatch('incrementAsync')
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('increment');
resolve(10);
}, 1000);
})
}
}
})
复制代码
我们可以在组件的某个合适的时机通过this.$store.state
来获取状态对象,以及通过this.$store.commit
方法触犯状态变更
this.$store.commit('increment')
复制代码
mapState辅助函数
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState
辅助函数帮助我们生成计算属性
//在单独构建的版本中辅助函数为Vuex.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
}
})
}
复制代码
当映射的计算属性的名称与state的子节点名称相同时,我们也可以给mapState传一个字符串数组。
computed: mapState([
//映射 this.count 为 store.state.count
'count'
])
复制代码
对象展开运算符
mapState
函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给computed
属性。但是自从有了对象展开运算符,极大地简化了写法
computed: {
...mapState({
"count"
})
}
复制代码
mapGetters辅助函数
mapGetters
辅助函数仅仅是将store中的getter映射到局部计算属性:
import {mapGetters} from 'vuex'
export default {
//...
computed: {
...mapGetters([
'evenOrOdd'
])
}
}
复制代码
如果你想将一个getter属性另取一个名字,使用对象形式:
mapGetters({
//把`this.doneEvenOrOdd`映射为`this.$store.getters.evenOrOdd`
doneEvenOrOdd: 'evenOrOdd'
})
复制代码
Mutation
更改Vuex的store中的状态的唯一方法是提交mutation。Vuex中的mutation与拥有一个字符串的**事件类型(type)和一个回调函数(handler)**的事件十分类似。这个回调函数就是我们实际进行状态更改的地方,并且它会接state作为第一个参数。
MapMutation辅助函数
你可以在组件中使用this.$store.commit('xxx')
提交mutation,或者使用mapMutations
辅助函数将组件中的methods映射为store.commit
调用(需要在根节点注入store
)。
import {mapMutations} from 'vuex'
export default {
//...
methods: {
...mapMutations('counter', [
'increment',
'decrement',
])
}
}
复制代码
Action
Action类似于mutation,不同在于:
- Action提交的是mutation,而不是直接变更状态。
- Action可以包含任意异步操作
MapAction辅助函数
import {mapMutations} from 'vuex'
export default {
//...
methods: {
...mapAction('counter', [
'incrementAsync'
])
}
}
复制代码
提交方式
//在组件内部
//以载荷形式分发
this.$store.dispatch('incrementAsync', {
amount: 10
})
//以对象形式分发
this.$store.dispatch({
type: 'incrementAsync',
amount: 10
})
复制代码
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。
为了解决以上问题,Vuex允许我们将store分割成模块(moudle)。每个模块拥有自己的state、mutation、action、getter,甚至是嵌套子模块一一从上至下进行同样方式的分割:
做一个购物车案例
有两个模块cart
和products
创建store文件夹
|--- store
|--- index.js
|___ modules
|--- cart.js
|___ products.js
复制代码
cart.js
如果希望你的模块具有更高的封装度和复用性,你可以通过添加namespaced: true
的方式使其成为带命名空间的模块
当模块被注册后,它的所有getter、action及mutation都会自动根据模块注册的路径调整命名。
export default {
//使当前模块具有更高的封装度和复用性
namespaced: true,
state: {
...
},
getters: {
...
},
mutations: {
...
},
actions: {
...
},
}
复制代码
products.js
export default {
//使当前模块具有更高的封装度和复用性
namespaced: true,
state: {
...
},
getters: {
...
},
mutations: {
...
},
actions: {
...
},
}
复制代码
index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import cart from './modules/cart';
import products from './modules/products';
export default new Vuex.Store({
modules: {
cart,
products,
}
})
//this.$store.state.cart //获取cart的状态
//this.$store.state.products //获取products的状态
复制代码
什么情况下我应该使用Vuex?
Vuex可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用Vuex可能是繁琐冗余的。确实如此——如果您的应用够简单,您最好不要使用Vuex。一个简单的store模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex将会成为自然而然的选择。引用Redux的作者Dan Abramov的话说就是:
Flux架构就像眼镜:您自会知道什么时候需要它
插件
日志插件
Vuex自带一个日志插件用于一般的调试:
import createLogger from 'vuex/dist/logger'
const store = new Vuex.store({
plugins: [createLogger({
collapsed: false,//自动展开记录的mutation
})]
})
复制代码
要注意,logger插件会生成状态快照,所以仅在开发环境使用。