Vuex快速入门
文档在这里: Vuex (vuejs.org)
1. Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态管理模式
让我们从一个简单的 Vue 计数应用开始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
image.png然而当多个组件共享状态(也就是数据)的时候,数据变动的来源变得不明显,逻辑变得繁琐不清晰。那么 vuex 就是将组件的共享状态抽取出来,以一个全局单例模式来管理的,专门为 Vue.js 设计的状态管理库,它利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
vuex
通俗地来讲,就是当页面应用过多且复杂,这个时候就适合用 vuex 在组件外部管理状态。
2. 安装
直接下载
npm install vuex --save
// npm 或 yarn
yarn add vuex
// 在一个模块化的打包系统中,必须显式地通过 Vue.use() 来安装 Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
CDN 引用
当使用全局 script 标签引用 Vuex 时,不需要以上安装过程。
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
3. State
创建一个 store,创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
现在,可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:
store.commit('increment')
console.log(store.state.count) // -> 1
mapState函数
mapState 函数返回的是一个对象。它可以将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。
其中
...
叫做 对象扩展运算符,可以将 mapState 返回的对象展开在所在位置上。
4. Getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数,如果需要的话也可以接受其他 getter 作为第二个参数。
实例中的
fullName()
函数可以换成 ...Vuex.mapGetters(['fullName'])
5. Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方。
在上面的两个例子之中已经使用到了 mutation 。
还可以向 mutation 传入额外的参数,当然也可以传递一个对象。
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
Mutation 需遵守 Vue 的响应规则
- 需要提前在 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用 Vue.set(obj, 'newProp', 123), 或者
- 以新对象替换老对象。
Mutation 必须是同步函数
每一条 mutation 都会被记录,如果使用异步,在 devtools 上将会很难捕捉前一状态与后一状态的快照。(实质上任何在回调函数中进行的状态的改变都是不可追踪的)
在组件中提交 Mutation
可以在组件中使用 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')`
})
}
}
6. Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
让我们来注册一个简单的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
使用es6可以简化代码为:
actions: {
increment ({ commit }) {
commit('increment')
}
}
分发Action
Action 通过 store.dispatch
方法触发:
store.dispatch('increment')
我们可以在 action 内部执行异步操作,也支持同样的载荷方式和对象方式进行分发:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
来看一个更加实际的购物车示例,涉及到调用异步 API 和分发多重 mutation:
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)
)
}
}
简单地来说,就相当于我让朋友帮我去买书,买成功了就跟这个管理员结账,如果不成功,就去找另一个来解决。那么这个 checkout
就相当于朋友。
7. Modules
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
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 的状态