ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
#### 再说一句 Vuex 的内脏由五部分组成:State、Getter、Mutation、Action 和 Module。关于这五个部分,我会分为多个章节来进行详细阐述,这一讲就先和大家一起彻底搞定 State 和 Getter。 当然,在实际应用中,这五个部分并不是必须的,你需要用到什么就添加什么。但是一般再怎么简单的 Vuex,也至少会由 State 和 Mutation 构成,否则你就该考虑 Vuex 是否有存在的必要了。 最后,温馨提示,文档示例代码使用了 ES2015 的语法,如果你还没了解过的话,先[戳此了解了解](https://babeljs.io/docs/learn-es2015/)。 #### 单一状态树 Vuex 使用的是**「单一状态树」**,根据官方的描述,可能有点懵圈,不过没关系,这里我们一起来详细了解下到底什么是**「单一状态树」**。抛开单一状态,我们先来看一下这里的树是什么意思。 ![](https://img.kancloud.cn/c0/8d/c08d4dba02ae43f6cd9f285e4be460b9_500x295.jpg) 组织架构 如上图是一个公司的组织架构,它的这种层级其实就属于一种树形的结构,总经理就是树的主干,其他各部门或者职业,都属于树的分支。 一般情况下,一个公司只会有这么一个树形架构,如果有两个平等的总经理,那么公司在管理上很可能就会出现矛盾,下面的人到底听谁的呢,是吧! 好,现在咱们再来看下官方所叙述的**「单一状态树」**: > 1、用一个对象`(主干)`就包含了全部的`(分支)`应用层级状态。 > 2、每个应用`(公司)`将仅仅包含一个 store 实例对象`(主干)`。 > > 单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。 #### State 我们再回过头来看一下之前那个简单的 Store 示例代码: ~~~jsx import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 0 } }) ~~~ 那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在[计算属性](https://cn.vuejs.org/guide/computed.html)中返回某个状态,如下: ~~~kotlin // 创建一个 Counter 组件 const Counter = { data() { return {} }, template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count } } } ~~~ > 每当 `store.state.count` 变化的时候, 都会重新求取计算属性,并刷新界面。 需要注意的是,如果你把 `store.state.count` 放在 data 中, `store.state.count` 的变化是不会主动触发界面刷新的,当然,也不能直接这样:`<div>{{ store.state.count }}</div>` ,因为在模板中是无法直接访问到 store 对象的,所以这样写无疑会报错。 这种模式依赖于全局的管理员 store,如果模块多了,意味着每个模块或者页面只要用到了这个 state 里面的数据,都得把 store 引入进来,这样的操作确实有点难受。当然,官方肯定是不允许有这样令人抓狂的操作出现的: > Vuex 通过 store 选项,提供了一种机制将状态从根组件 “注入” 到每一个子组件中(需调用 Vue.use(Vuex)): ~~~csharp const app = new Vue({ el: '#app', // 把 store 对象提供给 “store” 选项, // 这可以把 store 的实例注入所有的子组件 store, // 子组件 components: { Counter }, template: ` <div class="app"> <counter></counter> </div> ` }) ~~~ 通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。让我们更新下 Counter 的实现: ~~~kotlin const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } } ~~~ Vuex 的状态固然好用,但是也不要滥用: > 使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。 #### Getter 有时候,我们会发现 State 中的数据,并不是我们直接想要的,而是需要经过相应的处理后,才能满足我们的需求。 比如在一个组件中,我们需要把 state 中的日期 `date` 转换成星期几来展示: ~~~kotlin computed: { weekDate () { return moment(this.$store.state.date).format('dddd'); } } ~~~ 注意:这里的 [moment](http://momentjs.cn/) 是一个第三方日期处理类库,使用之前需要导入。 如果只有一个组件需要这样做还好,但如果在很多组件中,都需要这么转换的话,那就得在每一个组件中都需要把这个函数复制过去。而且,一旦产品经理心情不好,不想用星期几来显示,想直接用 `2018-10-30 11:12:23` 这种方式来显示日期,那你就得在所有用到它的组件中去更改日期格式化的方法,岂不难受至极。就算你把它单独抽取出来作为一个公共的函数,各种导入也麻烦,最重要的是不好统一管理。 所以,这个时候,Vuex 又引入了一个牛逼的玩意儿,**Getter**。我们可以把它当成 store 中的计算属性(computed)。 > 就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 我们看看这两个例子,着重注意下里面的注释: ~~~jsx const store = new Vuex.Store({ state: { date: new Date() }, getters: { // Getter 接受 state 作为其第一个参数 weekDate: state => { return moment(state.date).format('dddd'); } } }) ~~~ ~~~jsx getters: { // Getter 还也可以接收 getters 作为第二个参数 dateLength: (state, getters) => { return getters.weekDate.length; } } ~~~ 不但如此,Getter 还会将 store.getters 对象暴露出去,你可以以属性的形式访问这些值: ~~~css console.log(store.getters.weekDate) ~~~ 我们可以很容易地在任何组件中使用它: ~~~kotlin computed: { weekDate () { return this.$store.getters.weekDate } } ~~~ 现在需求又变了,每个模块要显示的 weekDate 的格式不一样,有的显示全部日期,有的需要显示星期几,怎么办? **好办,那就给 Getter 传参呗,但是怎么传呢?** 因为 Getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的,所以是不能直接 `store.getters.weekDate('MM Do YY')`,因为 weekDate 并不是一个函数,它仅仅只是一个属性而已。 那么既然属性不能传参,怎么办呢?那我们就想办法把这个属性变成一个函数不就行了。 ~~~jsx getters: { // 返回一个函数,就可以传参了 weekDate: (state) => (fm) => { return moment(state.date).format(fm ? fm : 'dddd'); } } ~~~ 使用如下: ~~~bash store.getters.weekDate('MM Do YY') ~~~ #### 写在最后 可能看过官方文档的童鞋会好奇,为什么没有讲解那些辅助函数,比如 `mapState`、`mapGetters`。别担心,后面会有专门的一个章节来进行讲解,因为我发现这些辅助函数(包括后面的 `mapMutations` 和 `mapActions`)都是为了解决同一个问题而生,只是形式不同罢了,所以还不如拆出来一起讲,或许效果会更好。 对于官方我个人觉得写得比较通俗易懂的地方,就直接引用了进来。如果有疑问或者对于我的理解有疑问的地方,欢迎留言。 当然,这种技术型的文章在不同阶段的人看来,肯定会有不一样的体会。我知道,肯定会有不足的地方,我也会继续慢慢的完善。如果文章对大家有帮助的话,欢迎点赞和转载,谢谢!