Vuex实践

前言

2017年对于Vue注定是不平凡的一年。凭借着自身简介、轻量、快速等特点,Vue俨然成为最火的前端MVVM开发框架。随着Vue2.0的release,越来越多的项目开始采用Vue作为他们的前端框架。而作为Vue生态中最重要的一环,Vuex渐渐进入大家的视野。

数据管理模式

在正式开始介绍Vuex之前,有必要介绍一下数据管理模式的前世今生。

当你在开发应用程序时,你一定会分解出很多组件进行开发,而各个组件之间想必在逻辑上多少是有关系的。那么组件之间的“通信”,就成了待解决问题。以前我们试图用事件广播来做,但随之而来的问题是,在应用不断的扩展、变化中,事件变得越来越复杂,越来越不可预料,以至于越来越难调试,越来越难追踪错误。这当然不是我们想要的,我们希望应用的各个部分都易维护、可扩展、好调试、能预测。于是数据管理模式应运而生。

image

图1

图1是最简单的组件关系,b是a的子组件,而c是b的子组件。在我们不引入任何数据管理模式之前,c组件要拿到a组件的数据只能由a先传给b,在由b传给c。如果组件树变得复杂,可想而知这将是一场灾难。看似严谨的父子结构其实严格限制了数据的流动方式。

image

图2

图2是最简单的数据管理模式,所有数据将统一交给全局store来管理。a和c组件现在直接修改store里的数据,并且通过mapState从store中抓取自己感兴趣的数据到自己的组件中。而b组件,如果它对a、c组件的数据毫无兴趣,则可以做到完全解耦。

image

图3

随着数据管理的进一步发展和演变,有一种叫单向数据流的东西冒了出来。图3就是一个表示“单向数据流”理念的极简示意。单向数据流要求各组件间的数据走向永远是单向的,可预期的。你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交Actions。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够更好地了解我们的应用。

这次我们的主人公Vuex可以说是单向数据流的最佳实践者。

Vuex是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。Vuex背后的基本思想,就是前面所说的单向数据流。图4就是Vuex实现单向数据流的示意图。

image

图4

接下来,我们将会更深入地探讨一些核心概念。让我们先从Store概念开始。

Store

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state) 。

安装 Vuex 之后,让我们来创建一个最简单的 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:

image

现在,你可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:

image

由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。

State

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

那么我们如何在 Vue 组件中展示状态呢?由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:

image

Getter

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

image

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

image

Getter 会暴露为 store.getters 对象:

image

Mutation

Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,payload作为第二个参数(额外的参数):

image

一条重要的原则就是要记住 mutation 必须是同步函数。

Action

Action 类似于 mutation,不同在于:

1.Action 提交的是 mutation,而不是直接变更状态。 

2.Action 可以包含任意异步操作。

让我们来注册一个简单的 action:

image

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

我们可以在 action 内部执行异步操作:

image

今天的目标

image

这是一个记忆小游戏,来自leftstick的vue-memory-game,github地址:https://github.com/leftstick/vue-memory-game。今天我们就这个小游戏来详细讲解下vuex的实现。

组件分解

image

我们根据分解图,先把要实现的组件挨个儿列出来:

  1. Game, 最外层的游戏面板
  2. Dashboard, 上面的logo,游戏进度,最佳战绩的容器
  3. Logo,左上角的logo
  4. MatchInfo, 正中上方的游戏进度组件
  5. Score, 右上角的最佳战绩组件
  6. Chessboard, 正中大棋盘
  7. Card, 中间那十六个棋牌
  8. PlayStatus, 最下方的游戏状态信息栏

目录结构

image

让我们聚焦红框中关于Vuex的这一部分。接下来我们将结合Card.vue和Chessboard.vue这两个核心组件进行。

store/index.js

image

store的创建并没有什么复杂的逻辑,而state就是这个项目需要维护的整个数据。初始化cards的结构我简要的整合了一下:

image

你可以选择初始化store的时候直接带进去,也可以像作者一样从入口处reset一遍数据,这里就不详细展开了。

Chessboard.vue

image
image

Card.vue

image

我在源码上增加了一些注释,方便大家理解。

mapGetter和mapActions只是一些辅助函数,可以减少一些this.$store和dispatch action之类比较重复的代码,这些辅助函数可以帮助vuex应用的更加简便。

相比与这些可有可无的噱头,我们更关心卡片被点击的时候发生了什么?this.flipCard()后数据流向了哪里?

数据流动

当卡片被点击时,vue component 实则 dispatch出了一个action,而我们的数据,作为载荷(参数),也只能随波逐流了。

他首先来到了第一站:actions,然而actions却说你不需要什么异步处理,可以去下一站了。(如果没有异步操作,其实可以少走一站)

image

actions处理函数

数据没有灰心,很快他就来到了mutaions。

mutaions欣喜若狂:你在这等着,待我处理好了你再上路。

image

mutations处理函数

不一会,数据就改头换面了,但如何告诉vue components我发生变化了呢?迷茫的数据找到了getter。getter哈哈大笑:你在我这的状态存储是响应式的,只要vue components在计算属性中应用了你,他们就能第一时间知道你的状态变更哦。

image

getter生成计算属性(如果没有派生出其他状态,可以不用getter)

image

components状态更新

结尾

通过这个记忆小游戏,希望大家对Vuex有一个基本的理解。如果你熟悉Redux的话,掌握Vuex应该不难,因为Vuex和Redux基本思想是一致的,实现方式也是大同小异。但Vuex只适用于Vue,没有Redux那么泛用罢了。关于Vue和Vuex的问题,欢迎交流~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • Vuex是什么? Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件...
    萧玄辞阅读 3,111评论 0 6
  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应...
    白水螺丝阅读 4,661评论 7 61
  • 安装 npm npm install vuex --save 在一个模块化的打包系统中,您必须显式地通过Vue.u...
    萧玄辞阅读 2,931评论 0 7
  • 文/若水 昨天还是阳光明媚 今天居然飘起了雨 簌簌地 阳台上 风信子绽放着 甘愿做春的使者 路面披着薄薄的一层冰 ...
    若水生花2020阅读 402评论 0 7