vuex是vue中单向数据流的一个状态管理模式,它可以集中存储管理应用中所有组件的状态,并且有一套相应的规则可以去预测数据的变化。类似与此的还有react中的redux,dva等状态管理模式。

一般我们的状态管理包含以下几个部分:

  • state 这是驱动页面变化的数据源
  • view state数据展示的视图
  • action 在view层用户操作数据变化的响应

vue中的数据流为单向数流

单向数据流在兄弟组件需要传参或者多个组件需要使用同一个状态并且多个组将都可以改变该状态时不易进行维护。

因此,我们采取的是将多个共用的状态抽离到一个全局单例中(实际上就是将组件的状态抽离出来进行单独管理),其实在redux和dva中,是将每个组件的状态抽离到它自己的单例状态中,并且这些单例状态之间是互通的。

vuex中数据流的一个大概的流程是,我们再视图层通过触发一个一个的action到mutations中,mutataions中改变对应的state,然后该state的变化会去影响所对应的视图层的html结构。

当然正如其他状态机模式一样,如果你不打算开发大型单页应用,也是没必要去使用vuex。

核心概念

vuex应用的核心就是store,store中包含着我们应用中的大部分状态,vuex的状态存储是快速响应的,当store中的state有变化时,相应的组件也会快速的更新。并且我们需要遵循vuex中的规则,无法直接去改变state,改变store中的状态的唯一途径就是通过commit.

通过每次的commit中所含的信息,我们可以轻松明确的去看到我们每次改变state的意图,这样也方便我们将来对数据的追踪。

state

由于vuex使用单一状态树,也就是一个应用中的state你可以全部放到这一个store中,但是如果你放置的状态过多的时候,这也是挺鸡肋的不是?我还没有去看vuex如何将状态和状态变更的事件分布到各个子模块中去。

mapState是vuex提供的简化数据访问的辅助函数,mapState函数返回的是一个对象,通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给computed属性。

当然,使用vuex并不意味着我们将所有的状态放入vuex,虽然将所有的状态放到vuex会使状态变化更显示和易调试,但是如果全部放到了全局Store中我们的代码会变得冗长和不直观,所以这些个东西还需要边开发边权衡吧。

Getter

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

computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}

vuex允许我们在store中定义“getter".就像计算属性一样,getter返回值会根据他的依赖被缓存起来,且只有当他的依赖值发生了改变才会被重新计算。

getter接受state作为其第一个参数:

const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})

getter会暴露store.getters对象:

getter类似于dva中的reducer。

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }

getter也可以接受其他getter作为第二个参数:

getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1

我们可以很容易的在任何组件中调用它:

computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
mapGetters辅助函数

mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性。

例如:

computed:{
...mapGetters(['getter1','getter2'])
}

按照上述写法,全局store中的getter1和getter2函数便可以在局部组件中使用了。

如果想将一个getter属性另取一个名字,使用对象形式:

computed:{
...mapGetters({
newGetter:"oldStoreGetter"
})
}

Mutation

更改Vuex的store中的状态的唯一办法是提交mutation.Vuex中的mutation非常类似于事件,这有点类似于dva中的effects,每一个mutation都会有一个字符串的事件类型和一个回调函数。这个回调函数就是我们实际进行状态更改的地方。并且他会接受state作为第一个参数:

 mutations: {
setAdminInfo(state,adminInfo){
state.adminInfo = adminInfo;
},
setCityList(state,cityList){
state.cityList = cityList
}
}

当然了,我们还不能直接调用mutation handler,我们想要执行此函数时,需要以相应的type调用store.commit方法:

当我们需要给mutation传参时,我们需要通过payload来进行,上述例子中的adminInfo就是我们要传的的参数,在vuex中叫payload,当然,官方的建议是payload尽量是一个对象,这样我们在使用的时候能够更好的去追踪数据流。

当然,更好的调用mutation的方式是commit包含type属性的对象:

store.commit({
type:'updateAdminInfo',
adminInfo:{
username:'allen',
password:'qwe123',
}
})

mutation需要遵守vue的响应规则

既然Vuex的store中的状态是响应式的,那么当我们变更状态时,监视状态的vue组件也会自动更新。这就意味着Vuex中的mutation也需要与Vue一样遵守一些注意事项:

  • 在store中初始化好所有的属性
  • 使用新对象替换老对象,即每次都返回一个全新的state对象,可以使用Object.Assign的方式。

使用常亮替代Mutation事件类型

使用常量替代mutation事件类型再各种flux实现中是很常见的模式。可以使我们整个项目的数据流向一目了然。

当然,另外重要的一点就是mutation必须是同步函数,当我们再debug一个app并且观察devtool的mutation日志时,每一条mutation被记录,都需要捕捉到前一状态和后一状态的快照。

我们可以在组件中使用this.$store.commit("example")提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为store.commit调用。

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')`
})
}
}

Action

Action类似于mutation,不同在于:

  • Action提交的是mutation,而不是直接变更状态。
  • Action可以包含任意异步操作。

下边来看一个简单的action的例子:

  state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})

Action通过store.dispatch方法触发:

store.dispatch('increment');

使用action进行触发mutation可以不用受必须同步执行的约束。我们可以在Action内部执行异步操作。

同样Action中也可以使用payload来传递参数

store.dispatch({
type:"increment",
amount:10
})

同时,Action通常是异步的,store.dispatch可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍然返回Promise.

现在,我们已经不需要去执行promise.then函数了,我们直接用async/await就可以了。

async action1({commit}){
commit('gotData', await getData());
}

Module

使用单一状态树,应用的所有状态会集中到一个比较大的对象中。当应用变得非常复杂时,store对象就会变得十分臃肿。

vuex允许我们将store分割成模块,每个模块拥有自己的state,mutation,acion,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 的状态
模块的局部状态

对于模块内部的mutation和getter,接受的第一个参数是模块的局部状态对象。同样的,对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState;

对于模块内部的getter,根节点状态会作为第三个参数暴露出来;

命名空间

默认情况下,模块内部的action,mutation,和getter是注册再全局命名空间的--这样可以使多个模块能够对同一个mutation或action作出响应。

当然,模块化中,我们可以使用namespaced:true的方式使其成为命名空间模块。当模块被注册之后,他的所有getter、Action、以及mutation都会自动根据模块注册的路径调整命名。启用了命名空间之后,便是将一个整体的store给分割成了一个个模块。

模块动态注册

在store创建之后,你可以使用store.registerModule方法注册模块:

// 注册模块 `myModule`
store.registerModule('myModule', {
// ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})

当然,我们也可以通过store.unregisterModule(moduleName)来动态卸载模块。但是我们无法使用此方法去卸载静态模块。


Vuex并不限制你的代码结构。但是,他规定了一些需要遵守的规则:

  • 应用层级的状态应该集中到单个store对象中。
  • 提交mutation是更改状态的唯一办法,并且这个过程是同步的。
  • 异步逻辑都应该封装在Action里面。

Vuex初识的更多相关文章

  1. Vuex ~ 初识

    状态:data中的属性需要共享给其他vue组件使用的部分(即data中需要共用的属性)   1.初识vuex直接来个小demo 下面操作都是基于vue-cli,如果不了解先学习下vue-cli 利用n ...

  2. Vue爬坑之vuex初识

    在 Vue.js 的项目中,如果项目结构简单, 父子组件之间的数据传递可以使用  props 或者 $emit 等方式 http://www.cnblogs.com/wisewrong/p/62660 ...

  3. 初识vuex

    1.简介 vuex是 vue官方推荐的一个状态管理器.当我们遇到很多状态改变时,组件之间的通信就会变得复杂,这时候vuex的强大就展现出来. 我们从vuex的原理以及vuex的api两个部分介绍vue ...

  4. 初识vuex vuex 的基本用法

    Vuex 是什么? 官方是这么说的:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 一个完 ...

  5. [20190620]日常学习记录(三)-初识promise及vuex

    在学习promise之前重温了Ajax的原生js实现, 在原生js中发送一个http请求首先new XMLHttpRequest() 然后定义状态变更事件 浏览器监听请求的状态,触发不同状态下相应的代 ...

  6. 前端笔记之Vue(四)UI组件库&Vuex&虚拟服务器初识

    一.日历组件 new Date()的月份是从0开始的. 下面表达式是:2018年6月1日 new Date(2018, 5, 1); 下面表达式是:2018年5月1日 new Date(2018, 4 ...

  7. vue(21)初识Vuex

    Vuex是做什么的? 官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式. 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. Vuex ...

  8. Vuex原来可以这样上手

    在Mvc模式大行其道的今天,后端通过各种Mvc框架实现视图与数据模型的隔离,而前端这方面也发展迅速.vue实现了Dom与viewModel双向绑定,使其视图的更新影响模型,模型的更新影响视图,你会不会 ...

  9. vue学习目录 vue初识 this指向问题 vue组件传值 过滤器 钩子函数 路由 全家桶 脚手架 vuecli element-ui axios bus

    vue学习目录 vue学习目录 Vue学习一之vue初识 Vue学习二之vue结合项目简单使用.this指向问题 Vue学习三之vue组件 Vue学习四之过滤器.钩子函数.路由.全家桶等 Vue学习之 ...

随机推荐

  1. java.util.HashSet

    Operations Time Complexity Notes add, remove, contains, size O(1) assuming the hash functions has di ...

  2. python multiprocessing.Process

    在使用Kafka-python时自己写的一个bug 我在一个进程的__init__中初始化了一个producer,但是一直不好用 但是在函数里直接new一个就好用了 why? 需要说明的是produc ...

  3. elastaticresearch 学习过程

    1.在Windows上安装了es 2.在chrome上装了sense 3.尝试创建 es 的模板

  4. 阿里云的oss使用技巧

    1初始化: 使用阿里云sdk包(php) 方法一:使用composer 加载sdk包 composer require aliyuncs/oss-sdk-php 或 "require&quo ...

  5. Sql 两个表left join 查左表最时间最大的一条记录显示

    http://bbs.csdn.net/topics/350135010 参考 select *  from a aa left join b bb on aa.id=bb.cid and bb.ad ...

  6. ADO.NET查询和操作数据库

    stringbuilder 类 stringbuilder类:用来定义可变字符串 stringbulider Append(string value)   在结尾追加 stringbuilder in ...

  7. 用 CSS3 做一个流星雨动画

    昨天 UI 提交过来一个登录页的设计稿,要求背景有一个流星雨动画,做完之后觉得挺有趣,分享一下~ 一.流星动画 首先创建一个 div 作为画布 <div id="stars" ...

  8. Robot Framework学习笔记(九)------创建资源和用户关键字

    一.测试套件下创建用户关键字 1.创建关键字测试套件右击->点击new user keyword,然后输入name,点击OK保存. 2.在用户关键字的edit点击settings,然后输入Arg ...

  9. Python新手需要掌握的知识点

    一.基础语法 1 变量 2 逻辑判断 3 循环 4 函数 二.数据结构 1 数字(加减乘除) 2 字符串(一串字符) 3 布尔 (真假) 4 元组 (不能修改的列表) 5 列表(Python的苦力,最 ...

  10. The `XXXX` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-game-desktop/Pods-game-desktop.release.xcconfig'. This can lead to prob

    The `game-desktop [Release]` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Po ...