Vuex状态管理详解

149 阅读8分钟

Vuex详解

学习目录 在这里插入图片描述

vuex介绍

解释 Vuex是一个专门为Vue.js应用程序开发的状态管理模式。 它采用集中式存储管理应用的所有组件,并以相应的规则保证状态以一种可预测的方式发生变化,配套的公共数据管理工具,它可以把一些共享的数据放到vuex中方便整个程序中的任何组件直接获取或修改我们的公共数据。

注意点 只需要贡献的才放到vuex上面,不需要贡献的数据依然放到组件data上

状态管理到底是什么? ~ 状态管理模式,集中存储管理,这些名词听起来非常高大上,让人捉摸不透 ~ 其实你可以简单地将其看成把许多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的Vue实例中,让其他组件也可以使用。 ~ 多个组件是不是就可以共享这个对象中所有的变量属性了呢!!

Vuex解决的问题

  1. 如果想要在子组件中使用父组件中的数据,那么必须通过父组件传递
  2. 如果想要在子组件中使用祖先组件中的数据,那么就要一层一层的传递
  3. 兄弟组件之间不能直接传递数据,如果兄弟组件之间想要传递数据,那么必须借助父组件,虽然通过借助父组件能够实现兄弟组件之间的数据传递,但是这种方法极为复杂,非常的不推荐。

设想 如果这就是vuex的,我们能不能自己封装一个const对象来进行管理呢? 当然可以,只是我们要先想想vueJS带给我们最大的便利是什么?那当然是响应式啦,如果你自己封装实现一个对象能不能保证它里面所有的属性都做到响应式呢?当然也可以,只是自己封装会很麻烦,不用怀疑,vuex就是为了提供这样一个多个组件间共享状态的插件,用vuex就可以满足以上需求。

什么状态时需要我们在多个组件间共享呢? 一、 大型项目,遇到多个状态,在多个界面间共享。 二、 用户的登陆状态,用户名称,头像,地理位置等等。 三、 商品的收场,购物车中的物品等等 这些状态信息都可以放在统一的地方,对它进行保存何管理,而且还是响应式的。

Vuex单界面到多界面状态管理切换

单界面状态管理切换 在单个组件中进行状态管理是一件非常简单的事情。 实例代码:

<template>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{counter}}</h2>
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
    return{
      message:"我是APP组件",
      counter:0
    }
  }
}
</script>

在这里插入图片描述 上图的种状态的理解

  1. state:不用多说,就是我们的状态(姑且可以当作data的属性)
  2. view:视图层,可以针对state的变化,显示不同的信息
  3. action:这里的action主要是用户的各种操作:点击,输入,移入,会导致状态的改变。

我们先来看不通过vuex父子组件传递数据的方式 app.vue代码:

<template>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{counter}}</h2>
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>
    <helloworld :counter="counter"/>
  </div>
</template>

<script>

import helloworld from './components/helloworld'
export default {
  name: 'App',
  components:{
    helloworld
  },
  data(){
    return{
      message:"我是APP组件",
      counter:0
    }
  }
}
</script>

<style>
</style>

helloworld.vue代码:


```html
<template>
  <h2>{{counter}}</h2>
</template>

<script>
export default {
    name:"helloworld",
    props:{
        counter:Number
    }
}
</script>

<style>

</style>

显示结果: 在这里插入图片描述 在这里插入图片描述 如上实例所示,父子组件虽然可以传递状态,但是,代码显得很是麻烦,如果子组件何祖宗组件传递呢!!接下来使用vuex进行传递状态。 使用vuex之前必须要安装vuex插件

第一步:npm install vuex --save 第二步:安装成功后,在src文件夹下创建store文件 在这里插入图片描述 编辑store文件的index.js文件 第三步:引入vuex import Vuex from 'vuex' 第四步:安装插件Vue.use(Vuex) 第五步:创建对象 const store = new Vuex.Store({}) 第六步:导出对象export default store 具体代码:

import Vue from "vue"
import Vuex from 'vuex'

//1.安装插件
Vue.use(Vuex)

//2.创建对象
const store = new Vuex.Store({
    state:{
        counter:100  //存入状态
    },
    mutations:{},
    actions:{},
    getters:{},
    modules:{}
})

//3.导出store对象
export default store

通过vuex使用状态数据

<template>
  <div id="app">
    <h2>--------------APP内容--------------</h2>
    <h3>{{message}}</h3>
    <h2>{{$store.state.counter}}</h2>
    <button @click="$store.state.counter++">+</button>
    <button  @click="$store.state.counter--">-</button>
   <h2>--------------hellovuex内容--------------</h2>
    <helloworld :counter="counter"/>
  </div>
</template>

显示效果何父传子一样: 在这里插入图片描述 多界面状态管理 vuex已经做好了但界面管理,但是多个界面呢?多个视图都依赖同一个状态(一个状态改了,多个界面需要进行更新),不同界面的Action都想修改同一个状态(Home.vue需要修改,Profile.vue也需要修改这个状态)。 也就是说对于某些状态(状态1,状态2,状态3)来说只属于我们某一个视图,但是也有一些状态(状态a,状态b,状态c)属于多个视图共享要维护的

  1. 状态1/状态2/状态3你放在自己的房间中,你自己管理自己用没有问题。
  2. 但是状态a/状态b/状态c/我希望交给一个大管家统一帮助我们管理~
  3. vue就是为了提供这个管理的大管家工具。

全局单例模式 4. 我们现在要做的就是间共享的状态抽取出来,交给一个大管家,进行统一的管理

  1. 之后,我们的每一个视图,按照规定好的规定,进行访问何修改等操作。
  2. 这就是vuex的背后思想。

vuex-devtools和mutations

vuex状态管理图例 在这里插入图片描述

下载 Devtools插件安装

网址:www.cr173.com/soft/106136… 下载完成后拖到Chome浏览器完成安装: 在这里插入图片描述 简单案例: 在这里插入图片描述 首先我们需要在某个地方存放我们的vuex代码:我们先创建一个store文件夹,并在其中创建一个index.js文件 在index文件中写入如下代码:

import Vue from "vue"
import Vuex from 'vuex'

//1.安装插件
Vue.use(Vuex)

//2.创建对象
const store = new Vuex.Store({
    state:{
        counter:100
    },
    mutations:{
        //定义方法
        increment(state){
            state.counter++
        },
        decrement(state){
            state.counter--
        }
    },
    actions:{},
    getters:{},
    modules:{}
})

//3.到处store对象
export default store

其次,我们让所有的vue组件都可以使用这个store对象,来到main.js文件中,导入store对象,并放在new Vue中,这样,在其他组件中我们就可以通过this.$store的方式,获取到这个store对象了。

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

app.vue代码如下:

<template>
  <div id="app">
    <h2>--------------APP内容--------------</h2>
    <h3>{{message}}</h3>
    <h2>{{$store.state.counter}}</h2>
    <button @click="addition">+</button>
    <button  @click="subtraction">-</button>
   <h2>--------------hellovuex内容--------------</h2>
    <helloworld :counter="counter"/>
  </div>
</template>

<script>

import helloworld from './components/helloworld'
export default {
  name: 'App',
  components:{
    helloworld
  },
  data(){
    return{
      message:"我是APP组件"
    }
  },
  methods:{
      addition(){
        this.$store.commit("increment")
      },
      subtraction(){
        this.$store.commit("decrement")
     }
    }
}
</script>

<style>
</style>

在这里插入图片描述

小结

这就是vuex最简单的方式,主要步骤小结:

  1. 提取出一个公共的store对象,用于保存多个组件中共享的状态。
  2. 讲store对象放置在new Vue对象中,这样可以保证所有的组件都可以使用到。
  3. 在其他组件中使用store对象中保存的状态: (1) 通过this.store.state属性的方式来访问状态。(2)通过this.store.state属性的方式来访问状态。 (2)通过this.store.commit("mutation中方法") 来修改状态。 注意事项:我们通过提交mutation的方式,而非直接改变store.state.count。这是因为vuex可以更明确地追踪状态的变化,所以不要直接改变store.state.count的值。

vuex-state单一状态树的理解

vuex核心概念 在这里插入图片描述 state 用来放置状态相关的信息,帮助我们管理状态信息 state单一状态树 vuex提出使用单一状态树,也可以说成是单一数据源 举个栗子做一个简单地类比:在国内,我们有很多的信息需要被记录,比如上学的时候的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口,医疗,文凭,房产等很多信息,这些信息被分散在很多地方进行管理,有一天你需要办理某个业务的时候,比如入户某个城市,你会发现你需要到各个对应的工作地点去打印,盖章,各种资料信息,最后到一个地方提交证明你的信息无误,这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作。 这个和在应用开发中做类比: 如果你的状态信息保存到多个store对象中,那么之后的管理和维护等等都会变的特别困难,所有vuex也使用了单一状态树来管理应用层级的全部状态,单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试的过程中,也可以非常方便的管理和维护。

Getter基本使用

类似于单个组件中的计算属性,对数据进行处理的时候进行使用。 在这里插入图片描述 方法二: 在这里插入图片描述 使用: 在这里插入图片描述 效果: 在这里插入图片描述 输出大于20岁的学生信息 store的index的代码:

import Vue from "vue"
import Vuex from 'vuex'

//1.安装插件
Vue.use(Vuex)

//2.创建对象
const store = new Vuex.Store({
    state:{
        counter:100,
        students:[
            {id:1,name:"wuyong",age:22},
            {id:2,name:"wuqi",age:22},
            {id:3,name:"wuman",age:26},
            {id:4,name:"zhanfyongfang",age:55},
            {id:5,name:"wulun",age:55},
        ]
    },
    mutations:{
        //定义方法
        increment(state){
            state.counter++
        },
        decrement(state){
            state.counter--
        }
    },
    actions:{},
    getters:{
        more20stu(state){
            return state.students.filter(s=>s.age>20)
        }
    },
    modules:{}
})

//3.到处store对象
export default store

App.vue里面的代码

<template>
  <div id="app">
    <h2>--------------APP内容--------------</h2>
    <h3>{{message}}</h3>
    <h2>{{$store.state.counter}}</h2>
    <button @click="addition">+</button>
    <button  @click="subtraction">-</button>
   <h2>--------------hellovuex内容--------------</h2>
    <helloworld :counter="counter"/>
    <h2>--------------getters先关信息内容--------------</h2>
    <h2>{{$store.getters.more20stu}}</h2>
 </div>
</template>

<script>

import helloworld from './components/helloworld'
export default {
  name: 'App',
  components:{
    helloworld
  },
  data(){
    return{
      message:"我是APP组件"
    }
  },
  methods:{
      addition(){
        this.$store.commit("increment")
      },
      subtraction(){
        this.$store.commit("decrement")
     }
    }
}
</script>

<style>
</style>

效果: 在这里插入图片描述

Getters作为参数和传递参数

按照上面的需求增加需求,希望获取学生大于20的人数 在这里插入图片描述

需求二,希望获取大于指定年龄的学生信息【返回一个函数,调用函数传递参数】 store代码 在这里插入图片描述 或者 在这里插入图片描述

app代码 在这里插入图片描述 效果:在这里插入图片描述

vuex-mutations的携带参数【状态更新】

Vuex的store状态的更新唯一方式:提交mutation

mutation主要包括两部分:

字符串的事件类型(type) 一个回调函数(handler),该回调函数的第一个参数就是state。

mutation的定义方式: 在这里插入图片描述 通过mutation更新 在这里插入图片描述 例子一、+5,+10按钮,传入参数 在这里插入图片描述 在这里插入图片描述 store文件夹下的index.js文件 在这里插入图片描述 效果: 在这里插入图片描述 例子二、传递对象添加学生信息 APP.vue文件在这里插入图片描述 在这里插入图片描述 store文件夹下的index文件 在这里插入图片描述 效果:

在这里插入图片描述

vuex-mutation的提交风格

上面通过commit进行提交的是一种普通的方式

vue还提供了另外一种风格,它是一个包含type属性的对象 在这里插入图片描述 特殊的提交方式:

//特殊的提交风格
       this.$store.commit({
         type:'incrementcount',
         count
       })

store下的index文件

 incrementcount(state,payloade){
            state.counter += payloade.count
        },

传递的count就不是数据,而是一个对象 在这里插入图片描述

vuex-数据的响应式原理

mutation响应规则 vuex的store中的state是响应式的,当state中的数据发生改变的时候,vue组件会自动更新。 这就是必须遵守的一些vuex对应的规则

提前在store中初始化好所需的属性 当给state中的对象添加新属性时,使用下面的

  1. 使用Vue.set(obj,'newProp',123)
  2. 用心对象给就对象重新赋值

例子:修改信息 app.vue代码: 在这里插入图片描述 在这里插入图片描述 store中index代码: 在这里插入图片描述 在这里插入图片描述 效果:在这里插入图片描述 在这里插入图片描述 解释: 在这里插入图片描述 上面的info里面的属性都会被加入到响应式系统中,而响应式系统会监听属性的变化,当属性发生变化的时候,会通知所有的界面中用到该属性的地方,让界面发生刷新。 如何添加数据实现响应式? 方式一、也可以通过Vue.set方法修改数据,将数据添加到响应式系统中: 调用方法:

Vue.set( target, key, value )

参数说明:

target:要更改的数据源,可以是对象或者数组 key:要更改的具体数据 value:重新赋的值

在这里插入图片描述 在这里插入图片描述 方式二、删除对象的属性,如果对象是响应式的,确保删除能够触发更新视图,这个方法主要用于避开vue不能检测到属性被删除的限制,但是很少使用! 调用方法:

Vue.delete(target,key)

参数说明:

target:要删除的数据源,可以是对象或者数组 key:要删除的具体数据

例子: 在这里插入图片描述 效果前: 在这里插入图片描述 效果后: 在这里插入图片描述

vuex-mutations的类型常量

概念、问题引入: 在mutation中,我们定义了很多事件类型,也就是其中的方法名称,当我们的项目增大的时候,vuex的管理的状态越来越多,需要更新状态的情况就越来越多,那么意味着mutation中的方法越来越多,方法过多,使用者需要花费大量的精力去记住这些方法,甚至是多个文件间来回切换,查看方法名称,甚至如果不是复制的时候,可能还会出现写错的情况。 解决问题: 在store文件夹下创建一个mutations.type.js文件,然后在里面导出定义的常量 在这里插入图片描述 此时的store中的index文件导入:

import {
    INCREMENT,
    DECREMENT
} from "./mutation.types"

index文件里的使用方法:

在这里插入图片描述

app.vue文件也需要导入:

import {
    INCREMENT,
    DECREMENT
} from "./mutation.types"

APP.vue使用方法: 在这里插入图片描述 在这里插入图片描述

vuex-actions的使用详情

问题引入: 在这里插入图片描述 你会发现state中的info数据一直没有被改变,因为它无法追踪到,所以通常情况下不要再mutation中进行异步操作

mutation同步函数: 通常情况下,vuex要求我们mutation中的方法必须是同步方法。主要的原因是当使用devtools时,可以devtools可以帮助我们捕捉mutation的快照,但是如果是异步操作,那么devtools将不能很好地追中这个操作什么时候会完成。 解决方法:不要在mutation中进行异步操作 但是使用action进行操作的时候还需要经过mutation进行操作; action具体操作代码如下: 在这里插入图片描述 APP.vue代码如下: 在这里插入图片描述 效果:在这里插入图片描述 需求:异步操作后还需要返回回调成功的信息 解决方法:index文件 在这里插入图片描述 APP.vue文件 在这里插入图片描述

vuex-modules的使用详解

问题引入为什么在VUEX中使用模块呢

module是模块的意思,vue使用单一状态树,那么也意味着很多状态都会交给vuex来管理,当应用变得非常复杂的时候,store对象就有可能变得相当臃肿,为了解决这个问题,vuex允许我们讲store分割成模块,而每个模块用用自己的state,mutation,getters,action等。

例如: 在这里插入图片描述 例子:在modules中定义一个模块 store中的index代码: 在这里插入图片描述 在这里插入图片描述 app.vue的代码: 在这里插入图片描述 效果: 在这里插入图片描述 需求二、修改姓名为指定的姓名,传参: store下的index文件代码:创建新的模块 在这里插入图片描述 在这里插入图片描述 app.vue代码: 在这里插入图片描述 组件方法传值: 在这里插入图片描述 效果前: 在这里插入图片描述 效果后: 在这里插入图片描述 注意: 在这里插入图片描述 增加的模块有三个属性:多了一个rootstate代表了根模块。 在这里插入图片描述

vuex-store文件夹的目录组织

当我们的vuex帮助我们管理过多的内容的时候,好的项目结构可以让代码更清晰 vuex文件分离框架图 分离: 在这里插入图片描述 分离后不要忘记从最上面导入分离的各个文件 在这里插入图片描述 分离文件浏览: actions.js文件 vuex文件分离

getters文件 vuex文件分离 mutation的文件 mutation的文件文件分离

VUEX完结【撒花】~