vuex 依赖于promise
import Vue from 'vue'
import Vuex from 'vuex'
/*
vuex 依赖于promise
*/
Vue.use(Vuex)
Stage
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
store.commit('increment')
console.log(store.state.count) // -> 1
为了在 Vue 组件中访问this.$storeproperty,你需要为 Vue 实例提供创建好的 store。Vuex 提供了一个从根组件向所有子组件,以store选项的方式“注入”该 store 的机制
new Vue({
el: '#app',
store: store,
// ...
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
})
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,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.filter(todo => todo.done)
}
}
})
通过属性访问
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作为第二个参数:
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
// ...
store.getters.doneTodosCount // -> 1
通过方法访问
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
// 注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mapGetters 辅助函数
mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
Mutation
vuex通过commit修改stage的值,你可以向store.commit 写入额外的入参
注意 ,Mutation 必须是同步函数
mutations: {
increment (state, n) { // 通常n会是一个对象
state.count += n
}
}
store.commit("increment", 10);
// 可以用这种风格
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit({
type: 'increment',
amount: 10
})
你可以在组件中使用this.$store.commit(‘xxx’)提交 mutation,或者使用mapMutations辅助函数将组件中的 methods 映射为store.commit调用(需要在根节点注入store)。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
Action
可以通过action处理异步,Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- Action 可以返回一个promise实例,完成链式调用action
```javascript
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
} }, actions: { increment (context) { // action也可以接收第二个参数state.count++
} } })// context.state
// context.getters
context.commit('increment');
// 可以返回promise实例
return Promise.resolver();
store.dispatch(‘increment’, { // action也可以接收第二个参数 amount: 10 })
// 以对象形式分发 store.dispatch({ type: ‘incrementAsync’, amount: 10 })
调用异步 API和分发多重 mutation:
```javascript
actions: {
checkout ({ commit, state }, products) {
// 把当前购物车的物品备份起来
const savedCartItems = [...state.cart.added]
// 发出结账请求,然后乐观地清空购物车
commit(types.CHECKOUT_REQUEST)
// 购物 API 接受一个成功回调和一个失败回调
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失败操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
你在组件中使用this.$store.dispatch(‘xxx’)分发 action,或者使用mapActions辅助函数将组件的 methods 映射为store.dispatch调用(需要先在根节点注入store):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
add() {
//...
},
...
},
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 的状态
this.$store.commit('a/add',10)
模块内部的 action,局部状态通过context.state暴露出来,根节点状态则为context.rootState:
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
命名空间
模块动态注册
import Vuex from 'vuex'
const store = new Vuex.Store({ /* 选项 */ })
// 注册模块 `myModule`
store.registerModule('myModule', {
// ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})
// 之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。
// store.unregisterModule(moduleName) 来动态卸载模块 只能卸载动态的,不能卸载静态的。
// 你可以通过 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store
在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过preserveState选项将其归档:store.registerModule(‘a’, module, { preserveState: true })。
当你设置preserveState: true时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。
dispatch 与 commit 的区别
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
dispatch 推送一个action
commit 推送一个mutation
注意事项
模块重用
有时我们可能需要创建一个模块的多个实例,例如:
- 创建多个 store,他们公用同一个模块 (例如当runInNewContext选项是false或’once’时,为了在服务端渲染中避免有状态的单例(opens new window))
- 在一个 store 中多次注册同一个模块
如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的data是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):
const MyReusableModule = {
state: () => ({
foo: 'bar'
}),
// mutation, action 和 getter 等等...
}
new Vue({
data: () => {
return {
...
}
}
})