来源:https://www.cnblogs.com/ITPower/p/14737901.html

  vuex官网: https://vuex.vuejs.org/zh/

必备:Vuex详细使用说明

  不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。什么是共享状态? 比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态。

  如果不对状态进行有效的管理,状态在什么时候,由于什么原因,如何变化就会不受控制,就很难跟踪和测试了。

  在软件开发里,有些通用的思想,比如隔离变化,约定优于配置等,隔离变化就是说做好抽象,把一些容易变化的地方找到共性,隔离出来,不要去影响其他的代码。约定优于配置就是很多东西我们不一定要写一大堆的配置,比如我们几个人约定,view 文件夹里只能放视图,不能放过滤器,过滤器必须放到 filter 文件夹里,那这就是一种约定,约定好之后,我们就不用写一大堆配置文件了,我们要找所有的视图,直接从 view 文件夹里找就行。

  根据这些思想,对于状态管理的解决思路就是:把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。

  Vuex是一个专门为Vue.js应用程序开发做状态管理的.

  他采用集中式存储管理应用的所有组件的状态, 并以响应的规则保证状态以一种可预测的方式发生变化.

  vuex官网说到的"状态管理模式", "集中式存储管理", 这些词和"状态管理"都是一个含义. 就是管理状态.

  我们通常会有很多组件, 组件之间可能会共享状态. 那么如何定义这个状态呢? 定义在某一个组件里面肯定是不合适的, 要定义在最外层.

  用vue生态圈来说, 有多个组件要共享状态, 通常状态我们用变量来表示, 也就是多个组件之间共享变量. 当共享变量变多, 我们就是用一个对象来存储, 这个对象就是存储共享状态的对象. 通常, 这个对象放在vue顶层的实例中. 其他各个组件都可以使用.

  而vue是响应式编程方式, 一个组件修改了状态, 其他组件能够实时响应么?这就是Vuex实现的功能.他的主要功能:

  管理状态: 因为是将各种状态保存在一个地方, 所以也叫集中式存储管理 或者 集中式装填管理响应式: 一个组件修改了状态, 其他组件能够实时响应

  不是所有的状态都要交给vuex来管理的, 只有在多界面之间共享的状态, 我们才将其交给vuex来管理. 比如:用户登录状态: 用户名, 头像, 昵称等等. 很多页面可能都会用到用户的基本信息, 像这些统一的信息, 我们就可以放在统一的地方进行管理了.token: 用户登录的令牌, 某些接口必须有令牌才能访问, 那么这些几口就需要共享token商品收藏, 购物车中的物品等. 我们在各个界面都可以添加商品搜藏, 都可以加购, 这时候, 就可以将其放入到vuex里面

  放在vuex中, 不仅能够共享状态, 还能够实时响应.

  Vuex 全局维护着一个对象,使用到了单例设计模式。在这个全局对象中,所有属性都是响应式的,任意属性进行了改变,都会造成使用到该属性的组件进行更新。并且只能通过 commit 的方式改变状态,实现了单向数据流模式。

  Vuex集成到了Vue的官方调试工具devtools extension, 提供了诸如零配置time-travel调试,状态快照导入导出等高级调试功能.

  之前我们遇到的都是在单界面进行状态管理. 单界面的状态管理有3个部分, 如下图所示:

  第一部分: state第二部分: view第三部分: action

  三部分是如何工作的呢? 通常状态我们会用一个变量来表示, 定义在组件的data属性中.

  然后,在页面中通过语法糖直接引用counter变量. counter的值就在页面中显示了.最后, 我们还可以在页面中增加action, 比如点击事件, 来改变state的状态.

  以上是在单页面中状态管理的流程.

  举个例子, 比如,我们有一个calculate.vue组件

  然后在App.vue中引入calculate.vue组件

  这时, 如果想要在App.vue中使用calculate.vue中定义的变量counter, 可以么?直接使用肯定会报错, 但Calculate和App两个组件的关系是父子组件, 可以使用父子组件变量传递的方式实现.

  如果不是父子关系呢? 需要如何实现呢?我们可以使用vuex.

  使用vuex, 首先需要安装vuex组件

  npm install vuex --save--save表示的含义是: 运行时需要

  组件安装好了, 下面就来看看怎么用吧.

  在src目录下新建/src/store/index.js. 通常, vuex都放在store文件夹里面. 然后在store下面创建一个文件index.js

  vuex是一个插件, vue-router也是一个插件, 插件的使用方式都是类似的.

  在store中定义了5个对象, 这5个对象是固定的. 每个对象的含义是什么呢? 后面在详细说.

  接下来在来看看2.2中多界面状态管理的问题. calculate组件中有一个counter, 在父组件App.vue中想要使用counter, 这时候, 这个counter就是一个公共的状态了, 可以将其定义在vuex中.

  下面来看看使用vuex的方式如何实现呢?

  这样就显示出来了counter变量了. 并且, 可以在calculate.vue或者App.vue, 或者任何其他组件中都可以直接使用. 如下图所示:

  接下来要实现+和-的操作.这个怎么实现呢? 我们可能会这么想

  $store.state.counter++或者$store.state.counter--不就可以了么?虽然, 这样也能达到效果, 但是Vuex官网推荐我们不要这样使用, 原因是, 这样操作完, 我们不能跟踪到状态的变化. 这是什么意思呢, 这就要来看看vuex的设计思想了.

  Vuex实质是单例模式的设计思想将共享的状态抽取出来, 交给大管家, 进行统一管理之后, 每一个视图, 按照规定好的规则, 执行访问或修改等操作.这就是vuex背后的思想.

  这里规定好的规则很重要. 规定好的规则是什么规则呢? 我们来看一下vuex官方给出的一个图

  这里面一共有5个元素Vue compontentsStateMutationsActionDevtools

  这几个部分都是做什么用的呢?

  在这个图里面绿色的部分是Vue compontents(Vue组件), Vue组件可以引用state变量, 还可以触发操作修改变量的值.

  State用来存储的是变量, 变量值可以直接渲染到Vue组件上, 但是约定好的, Vue组件不可直接修改State的值

  如果Vue组件想要修改state中的状态, 他不能直接修改State, 而是需要执行commit, 提交到Mutations, 由Mutations触发修改state的状态. 为什么要这样呢? 这就和Devtools有关系了.

  我们看到有一块灰色的Devtools, 这是什么呢? 这是Vue开发的一款浏览器插件. 这个插件可以帮助我们记录每次state中变量修改的状态, 为什么要记录state的状态呢?

  比如, 我们有多个组件同时修改vuex中的一个状态, 那么大家都来改, 最终这个值是谁改的呢? 如果没有记录状态变化, 那么我们就不知道是谁改的了. Vue提供的Devtools工具, 就可以用来记录每次修改的状态.

  但是, 如果我们直接修改组件, 那就没有经过Devtools的流程, Devtools也就记录不了了. 也就是直接从state修改变量值(红色箭头), 而不是走蓝色箭头的流程, 那么没有经过Devtools, Devtools也不能记录上修改的状态了.

  Action主要是用来进行异步处理操作的. mutations是用来处理同步操作的, 所以, vue组件想要修改变量的时候, 直接走mutations就可以了, 这样也可以通过Devtools来记录修改情况. 但是, 如果异步怎么办呢? devtools只能用来记录同步状态, 如果出现异步, 他就记录不了了. 所以, 这里多了一个action. action就是用来处理异步操作的. 当action处理完了以后, 再交给Mutations来处理, 这时候就是同步的操作了, Devtools也可以处理了.

  什么情况会进行异步操作呢?发送网络请求. 所以在Action模块指向了BackEnd, 向后端发送网络请求.

  下面我们就来安装Devtools打开google浏览器, 点击右上角三个点-->更多工具-->扩展程序-->打开Chrome网上应用店-->搜索Devtools, 如下图:

  找到VueJs的Devtools-->添加至Chrome, 如下图:

  然后安装插件即可.

  Chrome安装好Devtools以后,打开控制台, 在菜单栏最后多了一个vue, 点开可以看到如下界面:

  其中,第二个按钮是监控变量状态变化的.

  我们如果直接使用$store.state.counter++, 在Devtools是无法监控状态变化的,但是如果我们使用mutations就可以监控到状态变化.

  首先, 我们是要在页面实现+和-的逻辑, 如下图:

  这个逻辑在calculate.vue组件会用到, 在App.vue组件也会用到, 因此我们将其定义在vuex插件中. 在插件里面定义两个方法:increase()和decrease(), 修改变量counter的值

  这两个方法increase()和decrease(), 他们自带的参数就是state.

  在调用方如何定义呢? 也就是calculate.vue和App.vue中应该如何使用Vuex中定义的两个mutations方法呢?

  在调用方, 我们要使用this.$store.commit()的方式来提交变更.

  在左侧点击+或-,在右侧devtools都会记录每一次状态变化.

  Vuex 应用的核心就是 store(仓库), “store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

  Vuex的核心概念一共有5个StateGetterMutationsActionModules

  这几个概念: 其中State, Mutations, Action上面都有提高过. 下面重点来看看Getter和Modules. 在看Getter之前先来看一个概念: 单一状态树

  什么是单一状态树呢?

  比如我们的个人信息,社保信息存在社保系统里, 公积金信息存在公积金系统里, 医保信息存在医保系统里. 这有有好处也有坏处, 好处是信息更安全,彼此隔离; 坏处是要是办某一件事,想要所有的信息, 就要跑很多地方取.

  而单一状态树的含义就是将所有的信息都存在一个store中, 如果需要什么数据, 直接去那一个store中取就好了. 不要在系统中定义多个store, 这样不方便管理和维护.

  单一状态树的含义: 在一个项目只建一个store.

  Getter有些类似于compute计算属性. 什么时候使用计算属性呢? 当我们需要将一个属性的值经过计算以后显示出来, 这时候我们通常使用计算属性.

  Getter也是如此: 当一个state属性需要计算以后显示出来, 我们就可以使用Getter属性.比如现在要计算counter的平方.

  如果不使用计算属性,我们怎么做呢? 在calculate.vue组件里我们是这么写的.

  然后, 如果在App.vue中也要这么用, 就再来一段. 观察: 这代码很长, 不利于维护, 我们可以将其放到Getter中. 统一计算以后返回

  这里定义了一个计算平方的方法powerCounter(), 他的第一个参数是state, 所以, 我们可以直接拿到counter属性进行操作. 接下来在调用方如何调用呢?

  通过$store.getters.powerCounter获取计算属性.

  如果我们在另一个方法里想要使用其他getters计算方法怎么办呢? 在getters中定义的方法还有默认的第二个参数getters例: 计算counter的平方 + 100这时候我们可以怎么做呢? 如下定义了powerAndAdd方法, 其第二个参数默认是getters.

  我们在powerCounter已经做了平方的操作了, 接下来我们可以直接使用这个的结果来计算.在powerAndAdd方法中, 第一个参数依然是state, 第二个参数是getters. 我们可以通过getters获取到第一个方法powerCounter, 然后在第一个方法的基础上+100.

  在calculate.vue组件中调用powerAndAdd方法

  最终效果如下:

  在getter中, 前面说了, getters中定义的方法, 第一个参数是state, 第二个参数是getters. 这两个都是默认的. 如果我有一个方法想要传自定义参数怎么办呢?不能直接在getters后面添加, 可以使用匿名函数接收自定义参数. 比如下面的add方法

  在使用的时候, 直接传递两个参数就可以了,方法如下:

  Mutations 主要包含两部分一部分是事件类型(type)另一部分是回调函数(handler), 回调函数的第一个参数是state

  例如:

  我们可以理解为increase是事件类型type, 方法体是回调函数. 回调函数的第一个参数是state

  mutations方法定义好以后, 如果我们想要调用, 使用commit提交的方式调用

  在之前计算页面有一个+和-, 如果需要+5, +10, -100这时候怎么处理呢? 我们需要定义一个方法, 接收参数 在Mutation中如何定义参数, 又如何传递参数呢?

  在Mutation中有两种数据提交的方式

  我们来看看步骤:第一步: 在calculate.vue组件定义两个按钮, 一个+5, 一个加 +10

  在定义一个方法addCount()

  第二步: 在store中定义一个mutation方法, 并且接收一个参数, 如下increaseCount

  increaseCount()方法第一个参数是state, 我们可以用第二个参数来接收变量. 这和getter是不一样的, getter需要写一个匿名函数来接收自定义变量第三步: 在定义好的calculate组件中调用store的increaseCount方法

  在传递参数的时候, 我们将自定义参数放在第二个变量位置.

  第四步: 查看效果

  第一种数据传递的方式是具体的参数, 第二种数据传递的方式传递的是对象. 我们来看看第二种

  下面来对比比较:之前使用的是直接传递参数的方式

  使用对象传递参数怎么写呢?

  需要注意的是, 写法不同, 含义有略有区别.方式一传递到mutation中的是具体的参数值.方式二传递到mutation中的是一个对象.

  同样是在mutation中定义方法, 并接受一个参数obj

  第一种方式传递过来的是:

  可以看到传递过来是具体参数的内容

  第二种方式传递过来的是一个对象

  观察右侧控制台, 传输传递过来的是一个对象, 并且是所有参数的对象. 所以, 如果增加num数字, 需要获取obj.num

  Vuex的store的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.但是, 需要准守对应的规则

  比如: 我们修改info的name参数.先来看效果

  第一步: 在store/index.js的state中定义变量info, 并定义修改info的方法updateInfo

  第二步: 在calculate.vue中展示info内容, 并修改info的内容

  这里直接调用的是store中的mutation方法updateInfo(). 在updateInfo()方法中, 使用state.info["name"]=name的方式重置了name的值,并且在页面立刻响应式的看到了效果

  但是, 不是在任何情况使用state.info["name"]=name赋值都是响应式的, 我们来给info增加一个hobby试一试第一步: 在store/index.js中增加mutation方法, 添加爱好hobby, hobby属性之前在info中是没有的

  第二步: 定义方法修改hobby

  第三步: 看效果

  我们发现, 点击按钮添加hobby以后, 页面并没有响应式的添加hobby, 但是在右侧控制台看到$store.info中确实已经有hobby属性.

  这就是我们要说的Mutation修改state属性的第一个条件:要想实现响应式展示, 需要提前在store中初始化好属性. 如果有些属性是动态添加的, 提前不知道怎么办呢? 我们需要换一种方式添加

  来看看效果:

  还有一种方式: 就是使用完整的新对象给就对象赋值.

  总结:

  提前在store中初始化好需要的属性当给state中的对象添加新属性的时候,使用下面的方式方式一:使用Vue.set(obj, newProp, value)

  方式二: 用新对象给就对象赋值

  当我们需要删除属性的时候, 也是使用Vue.delete(obj, prop)可以做到响应式展示

  在mutation中, 我们定义了很多事件类型(也就是方法名), 当我们的项目变大时, vuex管理 状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多. 方法多了,名称就容易出错, 所以我们将Mutation中的常量提取出来. 放在一个公共文件中定义,下面来看看如何实现:以修改counter方法为例. 我们来将自增方法increase提取出来.

  第一步: 新增一个mutation-types.js 文件, 定义一个常量INCREASW

  第二步. 在store的mutation中使用常量定义方法名

  这里使用[]来定义方法名, 作为变量传入.

  第三步: 在calculate.vue中引入mutation-types并且使用commit提交到mutation

  这样就提取了变量. 当需要修改变量名的时候, 我们不用每个地方都修改, 只需要修改mutation-types中的变量名的值.

  通常情况下, Vuex要求我们Mutation中的方法必须是同步方法. 为什么呢?主要的原因是, 当我们使用devtools工具时, devtools工具可以很好的帮我们捕捉mutation的快照. 但如果是异步操作, 那么devtools将不能很好地追踪到这个操作是什么时候完成的.

  举个例子:我们将[修改name]这个动作进行异步处理. 放在setTimeout中,

  然后点击[修改name]按钮, 会发现将王五的名字改为赵六, 页面改了, 但是在devtools工具中没有改, 如下图:

  这个问题就是上面说的, 在Mutation中尽量不要执行异步操作, 要是执行异步操作, devtools可能跟踪不上.如果确实有异步操作, 那么就使用action. action的功能类似于Mutation, 但是它主要是处理异步操作的. 下面就来看看Action的使用

  上面已经说过了, action的用法和Mutation的用法类似. 但action主要是处理异步操作. 如何将写在mutation中的updateInfo方法中异步操作替换到action中实现呢?

  在mutation中定义的方法

  在action中定义的方法

  我们定义了一个和updateInfo对应的方法 aUpdate. 入参是context, 注意这里不是state了, 而是整个store. 异步操作定义在aUpdate方法中, 然后调用Mutation中的方法.注意: 这是官方要求的, action不要自己去修改state, 所以修改state的操作都在Mutation中进行.

  接下来, 在按钮[修改name]的时候, 重新定义click事件, 这次就不能直接指向mutation了, 而是要指向action.* 调用mutation的方法使用: this.$store.commit(...)* 调用action的方法使用: this.$store.dispatch(...)

  效果如下图所示:

  可以看到这会devtools识别了info信息的变化

  Module的含义是模块, 我们为什么要在Vue中引入模块呢?Vue使用的是单一状态树, 那么也就是说很多状态会交给vuex来管理当应用变得复杂是, store也会变得很臃肿,

  为了解决这个问题, Vuex允许我们将store分割成模块. 每个模块都拥有自己的states, mutations, actions, getters 等等

  写法如下:

  如上, 在store中定义了一个modules, 里面有两个模块a和b, a模块中定义了一套getters, states, mutations, actions, b模块也可如此定义

  那么定义好以后如何使用呢? 下面一个一个来看

  在store/index.js文件中定义一个moudules, 然后定义state

  如上展示, 如何调用module1中的message呢?

  在module1中增加getters

  前面介绍过getters中定义的属性, 就相当于computed计算属性.接下来看看如何调用getters中的计算属性呢?

  在调用的时候, 和state有些不同. 这里不需要指定modules模块名. 首先回去store中定义的getters查找, 抄不到再去modules1模块中查找,所以, 我们在定义的时候,尽量不要重名

  我们定义一个按钮来更新 message的值

  接下来看调用方, 定义一个按钮, 替换message的值. 然后changeMessage

  我们看到在调用mutation中的方法的时候, 直接使用的是 commit. 和调用store中mutation是一样的.这里还可以传递参数, 方式方法也和在store中一样

  我们在修改message信息的地方将其设置为异步修改, 写法如下:

  在actions中增加了一个setTimeout, 这就是异步的. 调用方在调用的时候, 需要使用dispatch指向actions的方法

  以上就是在modules中定义states, getters, mutations, actions的方法的使用

  至此Vuex的用法就全部完事了.