vuex
- 状态管理器
- 作为应用中所有组件的中央储存
- 只能以预定的方式去操作状态
- 把所有组件共享的状态抽取出来作为全局的单例,使得任何组件都能访问状态、触发动作
初始化
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
注入到App
const app = new Vue({
el: '#app',
// 会自动注入到每个子组件
store: store
// ...
})
// 接下来就可以在每个组件里使用$store来代替this.store
State
在组件中使用State里的数据(注意它是响应式的)
const Counter = {
template:<div>{{ count }}</div>
,
computed: {
count () {
return $store.state.count
}
}
}-
使用mapState一次性做几个computed
import { mapState } from 'vuex'export default { computed: { otherComputed() {}, ...mapState({ // ES2015的新语法,把对象分散为多个属性 count: state => state.count, // 接收的state参数里存了各种东西 // 上下两句话二选一 countAlias: 'count', }) } } // 如果属性的名字一样,可以传名称的数组给mapState mapState(['count'])
但并非所有状态都无脑放State里,也可以放组件自己的储存里
Getters
在全局提供可复用的"computed属性"供各个组件调用
-
简单使用
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => { // 接收第一个参数state用于调用state里的数据,第二个参数getters用于使用其他getter的结果
return state.todos.filter(todo => todo.done)
}
}
})computed: { doneTodosCount () { return this.$store.getters.doneTodosCount // 通过$store.getters调用 } }
-
同样也有mapGetters
import { mapGetters } from 'vuex'export default { computed: { ...mapGetters([ 'doneTodosCount', 'anotherGetter', ]) } } // 如果想改名字可以用对象 ...mapGetters({ doneCount: 'doneTodosCount' })Mutations
Mutations
提交Mutation是修改State的唯一方法
类似于事件(events)
-
基本使用
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state, n) { // 接收state作为第一个参数,后面是commit时候传递过去的参数
// mutate state
state.count++
}
}
})// 这样调用(实践中通常传一个对象作为第二个参数,这样可以传递多个属性) store.commit('increment', 10) // 第一个参数是mutation名,而10就是上面的n // 另外一种写法,此时整个对象会作为mutation函数的第二个参数 store.commit({ type: 'increment', amount: 10 })
-
使用mapMutations
import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ // 数组形式 'increment' // this.increment() = this.$store.commit('increment') ]), ...mapMutations({ // 对象形式 add: 'increment' // this.add() = this.$store.commit('increment') }) } }
-
注意事项:
- 在一开始的时候初始化State的每个属性
- 对对象添加属性时应该使用Vue.set方法
- Mutation应当是同步的,同时也不应该在异步方法里调用Mutation,否则会导致分不清先后顺序
Actions
提交的是Mutation而不是State
可以包含异步操作
-
简单使用
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) { // context有着store的所有属性和方法
context.commit('increment')
}
}
})// 使用解构来简化写法 actions: { increment ({ commit }) { commit('increment') } }
-
分发 Actions
actions: {
incrementAsync ({ commit }, payload) { // 进行异步操作。第二个参数是dispatch给他的
setTimeout(() => {
commit('increment')
}, 1000)
}
}// 两种触发方式 store.dispatch('incrementAsync', { amount: 10 }) store.dispatch({ type: 'incrementAsync', amount: 10 })
使用样例
actions: {
checkout ({ commit, state }, payload) {
const savedCartItems = [...state.cart.added]
// 做一些异步操作例如清空购物车
commit(types.CHECKOUT_REQUEST)
shop.buyProducts(
products,
// 成功回调
() => commit(types.CHECKOUT_SUCCESS),
// 失败回调
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}-
mapActions
import { mapActions } from 'vuex'export default { // ... methods: { ...mapActions([ 'increment' // this.increment() = this.$store.dispatch('increment') ]), ...mapActions({ add: 'increment' // this.add() = this.$store.dispatch('increment') }) } }
处理异步的回调
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => { // 返回了一个Promise
setTimeout(() => {
commit('someMutation')
resolve() // 函数内做好事情后resolve
}, 1000)
})
},
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => { // 这里用then
commit('someOtherMutation')
})
}
}
Modules
把Store划分成若干个 Module,避免臃肿
每个Module有自己的State、Mutation、Action、Getter等
-
二合一的state
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ // 包含2个子module modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA's state store.state.b // -> moduleB's state
-
本地State
- Module里的Getter等拿到的是Module自己的State,
- 如果要访问根State,在Action里要使用context.rootState,而在Getter里rootState作为第三个参数传入。
-
名字空间
默认情况下不同Module里的Mutation等都注册在全局名字空间
可以使用前缀或者后缀来区分
-
样例
// types.js// 定义名字常量并添加前缀 export const DONE_COUNT = 'todos/DONE_COUNT' export const FETCH_ALL = 'todos/FETCH_ALL' export const TOGGLE_DONE = 'todos/TOGGLE_DONE' // modules/todos.js import * as types from '../types' // define getters, actions and mutations using prefixed names const todosModule = { state: { todos: [] }, getters: { [types.DONE_COUNT] (state) { // ... } }, actions: { [types.FETCH_ALL] (context, payload) { // ... } }, mutations: { [types.TOGGLE_DONE] (state, payload) { // ... } } }
-
注册Module
store.registerModule('myModule', {
// ...
})// 调用 store.state.myModule.