作者:滴滴公共前端团队

前言:

最早我们在设计《Vue.js权威指南》这本书的时候也一直思考要不要加入 Vuex 相关的内容,也有很多同学抱怨说我们没有加入这个章节。

其实整体我们应用的还是比较早,也在 1.0 和 2.* 都踩了一些坑,但是也不期望大家在任何复杂不复杂的场景里面滥用 Vuex。

后面我们在 vue 2.0 全家桶源码分享系列里面也分享了一篇《Vuex 2.0 源码分析》,没有看过的同学可以在文末链接查看

正文:

Vuex 作为中大型 Vue 应用中的“御用”集中数据管理工具,在滴滴很早就得到了广泛使用。本文旨在以尽可能简洁的文字向读者展示:如何在一个颇具规模的 Vue 应用中组织和管理 Vuex 的代码

注:虽然目前 Vuex 的最新版本已经来到 2.x。2.x 在1.0 的基础上进行了一些优化,提升了命名的语义化以及 增强了模块的可移植性和可组合性,但基本思想和架构并没有改变。

本文基于 Vuex 1.0 版本,读者大可不必担心出现类似 Angular 1.x 升级到 2.x 式的断崖式更新。

首先,介绍一下项目的背景:
一个采用 Vue.js 编写的富交互的 H5 编辑器,由于各个组件中的数据交互繁多,页面的生成也极度依赖存储的状态,使用 Vuex 进行管理便势在必行。
项目引入 Vuex 的方式如下:

import App from 'components/home/App'
import store from 'vuex/editor/store'// 在 Vue 实例的初始化中声明 store。
new Vue({el: 'body',components: {App},store
})复制代码

在根实例中注册 store 选项,这样该 store 实例会注入到根组件下的所有子组件中,方便后面我们在每个子组件中调用 store 中 state 里存储的数据。

然后看一下 vuex 文件夹下的目录,后面我们会逐个分析每个文件的作用:

└── editor├── mutation-types.js├── actions│   └── index.js├── mutations│   └── index.js├── plugins│   └── index.js├── state│   └── index.js└── store└── index.js复制代码

创建 store 对象的代码放在 vuex/editor/store/index.js 中,如下所示:

// vuex/editor/store/index.js
import Vuex from 'vuex'
import state from 'vuex/editor/state'
import mutations from 'vuex/editor/mutations'
import { actionLogPlugin } from 'vuex/editor/plugins'const store = new Vuex.Store({state,mutations,plugins: [actionLogPlugin]
})export default store复制代码

这里又声明了 state 和 mutations 对象,以及声明了使用到的 plugins。plugins 后面再说,先看 state 和 mutations,相信各位读者已经对 Vuex 中各个部件的作用已经了如指掌,但是为防遗忘,还是贴一下这张图吧:

state 是用于存储各种状态的核心仓库,让我们一瞥 vuex/editor/state/index.js 中的内容:

// 编辑器相关状态
const editor = {...
}// 页面相关状态let page = {...
}const state = {editor,page
}export default state复制代码

state 中存储了 editorpage 两个对象,用于存储不同模块的状态。需要说明的是,这里完全可以使用模块机制将其拆开,在 editor.js 里存储编辑器相关的 state 和 mutations,在 page.js 中存储页面相关的 state 和 mutations,以使结构更加清晰。不过这里没有使用模块机制,由于模块数量并不多,也是完全可以接受的。

这些 state 需要反映到组件中。

跳过官方文档中对为何不使用计算属性的解释,我们直接来看最佳实践:在子组件中通过 vuex.getters 来获取该组件需要用到的所有状态:

// src/components/h5/Navbar.vue...
export default {data () {return {...}},methods: {...},vuex: {actions: {...},getters: {editor(state) {return state.editor},page(state) {return state.page},...}}
}复制代码

vuex.getters 对象中,每个属性对应一个 getter 函数,该函数仅接收 store 中 state,也就是总的状态树作为唯一参数,然后返回 state 中需要的状态,然后在组件中就可以以 this.editor 的方式直接调用,类似计算属性。

再看一下 vuex/editor/mutations/index.js 中的内容:

import * as types from '../mutation-types'const mutations = {[types.CHANGE_LAYER_ZINDEX] (state, dir, index) {...},[types.DEL_LAYER] (state, index) {...},[types.REMOVE_FROM_ARR] (state, arr, itemToRemove) {...},[types.ADD_TO_ARR] (state, arr, itemToAdd) {...},[types.DEL_SCENE] (state, index) {...},...
}export default mutations复制代码

具体业务逻辑这里不展开,mutations 中主要就是定义各种对 state 的状态修改。每个 mutation 函数接收第一个参数为 state 对象,其余参数则为一路从组件中触发 action 时传过来的 payload。所有的 mutation 函数必须为同步执行,否则无法追踪状态的改动。

注意到,这里引入了 mutation-types.js。该文件主要作用为放置所有的命名 Mutations 的常量,方便合作开发人员厘清整个 app 包含的 mutations。在采用模块机制时,可以在每个模块内只引入相关的 mutations,也可以像本项目一样使用 import * as types 简单粗暴地引入全部。

mutation-types.js 中内容大致如下:

export const CHANGE_LAYER_ZINDEX = 'CHANGE_LAYER_ZINDEX'
export const DEL_LAYER = 'DEL_LAYER'复制代码

然后我们来到 actions,照例先看一下 vuex/editor/actions/index.js 中的内容:

import * as types from '../mutation-types'export function delLayer( { dispatch }, index) {dispatch(types.DEL_LAYER, index)
}export function delScene( { dispatch }, index) {dispatch(types.DEL_SCENE, index)
}export function removeFromArr( { dispatch }, arr, itemToRemove) {dispatch(types.REMOVE_FROM_ARR, arr, itemToRemove)
}export function addToArr( { dispatch }, arr, itemToAdd) {dispatch(types.ADD_TO_ARR, arr, itemToAdd)
}复制代码

actions 的主要工作就是 dispatch (中文译为分发)mutations。初入门的同学可能觉得这是多此一举,actions 这一步看起来完全可以省略。

事实上,actions 的出现是为了弥补 mutations 无法实现异步操作的缺陷。所有的异步操作都可以放在 actions 中,比如如果想在调用 delScene 函数 5 秒后再分发 mutations,可以写成这样:

function delScene ({ dispatch }, index) {setTimeout(() => {dispatch(types.DEL_SCENE, index)}, 5000)
}复制代码

触发 mutations 的代码不会在组件中出现,但 actions 会出现在每个需要它的组件中,其也是连接组件和 mutations 的桥梁(额,另一条桥梁是 state,见上面那张经典老图)。在子组件中引入 actions 的方式类似 state,也是注册在 vuex 选项下:

// src/components/h5/Navbar.vue
...import { undoAction, redoAction,togglePreviewStatus,...
} from 'vuex/editor/actions'export default {data () {return {...}},methods: {...},vuex: {actions: {undoAction,redoAction,togglePreviewStatus,...},getters: {...}}
}复制代码

这样,组件中可以直接调用各个 actions,比如 this.togglePreviewStatus(status),等价于this.togglePreviewStatus( this.$store, status)(还记得我们在 actions 中定义的各个函数的第一个参数是 store 吗?)。这是最基本的使用 actions 的方式,在此基础上你还可以玩出别的花样来,比如给 actions 取别名、定义内联 actions、绑定所有 actions 等,具体用法参见官方文档。

回过头去看 vuex 文件夹下的目录结构,发现还有一个 plugins 我们没有介绍。老规矩,先看一下 vuex/editor/plugins/index.js 中的内容:

...
export function actionLogPlugin(store) {store.subscribe((mutation, state) => {// 每次 mutation 之后调用// mutation 的格式为 { type, payload }...})
}复制代码

核心部分在于采用 store.subscribe 注册了一个函数。

该函数会在每次 mutation 之后被调用。这里 actionLogPlugin 函数完成的是记录每次 mutation 操作,实现撤销重做功能。具体实现逻辑此处不作赘述。

后续我们也会深入地给大家分享 vuex 应用相关的内容

附:

Vuex 2.0 源码分析知乎地址:zhuanlan.zhihu.com/p/23921964

「掘金技术征文」活动:gold.xitu.io/post/58522d…


欢迎关注DDFE
GITHUB:github.com/DDFE
微信公众号:微信搜索公众号“DDFE”或扫描下面的二维码

Vuex 实战:如何在大规模 Vue 应用中组织 Vuex 代码 | 掘金技术征文相关推荐

  1. 在 vue 组件中查看 vuex 定义

    原文:在 vue 组件中查看 vuex 定义 在进行 vue 项目开发的时,如果项目中用到了 vuex 做状态管理,经常需要查看 store 里面定义的状态属性.但是在 vue 组件中引用这些 vue ...

  2. vue组件中数据共享——vuex

    目录 vuex是什么? 使用vuex统一管理状态的好处 什么样的数据适合存储到vuex中 安装vuex 使用vuex vuex核心概念 组件访问state中数据的第一种方式: 组件访问state中数据 ...

  3. Vuex---在 Vue 组件中获得 Vuex 状态state

    Vuex使用单一状态树(一个对象就包含了全部的应用层级状态),它作为唯一数据源存在,每个应用仅仅有一个store实例. 单一状态树使得我们能够直接定位任一特定的状态片段,在调试过程中也能轻易地取得整个 ...

  4. 在vue项目中引用vuex状态管理工具

    在vue项目中引用vuex状态管理工具 一.vuex是什么? 二.使用步骤 1.引入库 2.在main.js文件引入配置 3.配置store/index.js文件 4.获取state数据 5.获取ge ...

  5. Flutter完整开发实战详解(二、 快速开发实战篇) | 掘金技术征文

     作为系列文章的第二篇,继<Flutter完整开发实战详解(一.Dart语言和Flutter基础)>之后,本篇将为你着重展示:如何搭建一个通用的Flutter App 常用功能脚手架,快速 ...

  6. 我是如何在天猫、蚂蚁金服、百度等大厂面试中被拒的 | 掘金技术征文

    本人16年毕业于普通二本院校网络相关专业,工作经验两年半,目前就职业于一家普通民营企业. 由于非985.211学历硬伤,校招进大厂的门槛远高于同届985.211的毕业生.于是乎,从毕业到现在经历了三家 ...

  7. iOS 开发中的得力鲁班尺 - SnapKit | 掘金技术征文

    SnapKit 先来谈谈何为 DSL DSL(Domain Specific Language),即领域特定语言,指的是一种针对特定问题的编程语言,与此相对便是 GPL(General Purpose ...

  8. Agora SDK 在Android中的使用(在线视频通话)| 掘金技术征文

    首先声明本文是Agora SDK入门的小白文章 一.集成 1.注册账号创建项目 其中最重要的要数 App ID 了 2.下载Agora SDK 二.学会看示例代码(可跳过) 1.整体了解项目结构(1v ...

  9. 火眼金睛识别公司招聘中给你埋下的那些“坑”| 掘金技术征文

    版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请标明出处: juejin.im/post/5a5249- 本文出自 AWeiLoveAndroid的博客 [前言]又到了年末,有一些公司在招 ...

  10. 在vue项目中引入vuex(全局状态管理器)

    目录 Vuex是什么? State Getter Mutation Action Module 项目结构 Vuex是什么? Vuex是一个专为Vue.js应用程序开发的状态管理模式.它采用集中式存储管 ...

最新文章

  1. android与PC,C#与Java 利用protobuf 进行无障碍通讯【Socket】
  2. ASP-JSP-NET 清空IE缓存
  3. 新手探索NLP(四)
  4. 安卓代码迁移:ActionBarActivity: cannot be resolved to a type
  5. html5 indexeddb 排序,html5 – 在IndexedDB中,有没有办法进行排序复合查询?
  6. 华为机试HJ50:四则运算
  7. Hutool工具里,POST方法,body中传参的几种调用方法
  8. vue 配置跨域访问
  9. linux磁盘常用操作命令
  10. 二叉树的遍历以及遍历算法的应用(链式存储结构)
  11. java 415_@RequestBody接受参数报415错误
  12. Verilog语言注意事项
  13. 网络攻防技术(摆烂一天)
  14. java报错Error attempting to get column ‘XXX’ from result set. Cause: java.sql.怎么解决
  15. 主流实时流处理计算框架Flink初体验
  16. 为啥不招北大清华的?
  17. Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.Generi
  18. PPC气箱脉冲除尘器
  19. Linux X11远程图形桌面显示
  20. 2012搜狗校园招聘笔试题

热门文章

  1. django mysql处理_利用Django去操作数据库并完成简易的登录及编辑功能
  2. 契税申报期限_纳税申报的5个小常识,会计不知道,真不适合干会计 D1
  3. ci github 通知_GitHub 欢迎一切 CI 工具
  4. c语言寻找子字符串拷贝,C语言:字符串拷贝(截取)、查找
  5. 「读懂源码系列3」lodash 是如何实现深拷贝的(上)
  6. Kubernetes Ingress 日志分析与监控的最佳实践 1
  7. 17.基于scrapy-redis两种形式的分布式爬虫
  8. 2-Linux C语言指针与内存-学习笔记
  9. 对JVM GC进一步的实例解析
  10. SQL Server 中的例程分析