State(状态)
单一状态树
Vuex 使用 单一状态树 - 是的,这单个对象包含了全部的应用级状态,然后作为 "单一数据源"。这也意味着,每个引用通常只有一个 store。使用单状态树,可以简单地定位到状态的某些部分,也让我们容易保存前应用状态的快照,用来调试程序。
单状态树和模块化并不冲突 —— 我们会在后面章节讨论如何将 state 和 mutation 分散在多个子模块中。
在 Vue 组件中访问 Vuex 的状态
那么我们如何在 Vue 组件中展示 store 中的状态呢?因为 Vuex 存储的状态是响应式的,所以要从中"获得"状态的最简单的方式就是在 computed property(计算属性) 内返回某些状态:
// 创建一个计数器组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
只要 store.state.count
发生变化,就会引起计算属性重新求值,接着触发关联的 DOM 更新。
不过,这种模式使得组件依赖了全局的 store 单例。当使用模块化工程时,需要在用到全局状态的每个组件里 import 这个 store,同时在测试组件的时候也需要模拟这个 strore。
Vuex 提供一种机制,在根组件配置 store
选项,则会将该 store 『注入』到根组件以及其下的所有子组件(通过 Vue.use(Vuex)
开启该机制):
const app = new Vue({
el: '#app',
// 使用 "store" 配置项来提供 store
// 这个 store 实例将会被注入到每个子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
通过在根实例配置 store
选项,这个 store 就会被注入到根实例下的所有子组件,并且在子组件中通过 this.$store
来访问它。我们来更新一下 计数器
的实现:
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
mapState
工具
当一个组件用到 store 的多个状态属性或 getter,那么就要逐个定义这些计算属性,这是重复又繁琐的工作。为了解决这个问题,我们可以使用 mapState
工具,帮助我们生成计算属性 getter 方法,节省一些工作量:
// 独立发布版的工具方法会暴露为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可以让代码非常简洁
count: state => state.count,
// 想通过 `this` 访问本地状态,就得用一个普通的方法
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
对象解构操作符
要注意的是,mapState
返回一个对象,我们如何将它结合其他本地计算属性一起使用呢?一般来说,我们会用一个工具方法来将多个对象融合为一个对象,于是就可以将这一个对象传递到 computed
选项。不过,要是用上 对象解构操作符(ECMASCript 草案 stage-2 阶段)的话,就能极大的简化语法:
computed: {
localComputed () { /* ... */ },
// 用解构操作符,将该对象跟外面的属性混合
...mapState({
// ...
})
}
组件依然有本地状态
使用 Vuex 并不意味着你一定要把 所有 状态都放到 Vuex。虽然把更多的状态放到 Vuex 会让状态变化变得更明确和可调试,但是有时候会让代码变得啰嗦复杂。如果某些属性只属于某个组件,把它作为本地状态就好了。你得根据应用的开发所需作权衡取舍。