通俗易懂且全面的Vuex

4 篇文章 0 订阅
1 篇文章 0 订阅

一、Vuex是做什么的?

  • 官方解释: Vuex是一个专为Vuejs应用程序开发的 状态管理模式

    • 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以-种可预测的方式发生变化。
    • Vuex 也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试。状态快照导入导出等高级调试功能。
  • 状态管理到底是什么?

  • 管理什么状态呢?

    • 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
    • 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一 个对象里面
    • 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
    • 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
  • 等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗 ?

    • 当然可以, 只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
    • 如果你自己封装实现一个对像能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
    • 不用怀疑, Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。
  • 但是,有什么状态时需要我们在多个组件间共享的呢?

    • 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
    • 比如用户的登录状态、用户名称、头像、地理位置信息等等。
    • 比如商品的收藏、购物车中的物品等等。
    • 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。
  • OK ,从理论上理解了状态管理之后,让我们从实际的代码再来看看状态管理

    • 毕竟,Talk is cheap, Show me the code.(来自Linus)

在这里插入图片描述

二、单界面的状态管理

  • 我们知道,要在单个组件中进行状态管理是一件非常简单的事情

    • 什么意思呢?我们来看下面的图片。
      请添加图片描述
  • 这图片中的三种东西,怎么理解呢?

    • State :不用多说,就是我们的状态。( 你姑且可以当做就是data中的属性)
    • View :视图层,可以针对State的变化,显示不同的信息。( 这个好理解吧? )
    • Actions :这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变。
  • 写点代码,加深理解:

    • 看下右边的代码效果,肯定会实现吧?
      <template>
        <div>
          <h2>{{counter}}</h2>
          <button @click="counter++">+</button>
          <button @click="counter--">-</button>
        </div>
      </template>
      
      <script>
        export default {
          name: 'Hello world!',
          data: {
      	    return: {
      	    counter: 1000
          	}
          }
        }
      </script>
      
      <style scoped>
      </style>
      
    • 在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter.
    • counter需要某种方式被记录下来,也就是我们的State.
    • counter目前的值需要被显示在界面中,也就是我们的View部分。
    • 界面发生某些操作时(我们这里是用户的点击,也可以是用户的input) , 需要去更新状态,也就是我们的Actions
    • 这不就是上面的流程图了吗?

三、多界面状态管理

  • Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
    • 多个试图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
    • 不同界面的Actions都想修改同一个状态( Home.vue需要修改, Profile.vue也需要修改这个状态)
  • 也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个视图 ,但是也有一些状态(状态a/状态b/状态c)属于多个视图共同想要维护的
    • 状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。
    • 但是状态a/状态b/状态c我们希望交给一个大管家统一帮助我们管理! ! !
    • 没错, Vuex就是为我们提供这个大管家工具
  • 全局单例模式(大管家)
    • 我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
    • 之后,你们每个视图,按照我规定好的规定,进行访问和修改等操作。
    • 这就是Vuex背后的基本思相

Vuex状态管理图例

  • 起在来看一副官方给出的图片

在这里插入图片描述

既然概念清楚了,那就开始学习!!

我们先安装vuex响应跟踪插件
我用的是Google浏览器,下面的也是Google浏览器的教程,其他浏览器自行百度
用Google搜索比百度好太多,具体就不细说了,但是得用个梯子
文明上网,咱不发梯子教程,自己想办法去

第一步:
请添加图片描述
第二步:
请添加图片描述
第三步:
请添加图片描述
点进去安装就可以了

然后是使用:
在这里插入图片描述
如果没出来就重启浏览器
然后:
在这里插入图片描述
可以了!

四、Vuex核心概念

  • Vuex有几个比较核心的概念:
    • State
    • Getters
    • Mutation
    • Action
    • Module
1.state单一状态数
  • Vuex提出使用单一状态树, 什么是单一状态树呢?
    • 英文名称是Single Source of Truth ,也可以翻译成单一数据源。
  • 但是,它是什么呢?我们来看一个生活中的例子。
    • OK,我用一个生活中的例子做一 个简单的类比。
    • 我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭房产记录等等(还有很多信息)。
    • 这些信息被分散在很多地方进行管理,有一 天你需要办某个业务时(此如入户某个城市) ,你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。
    • 这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护 ,当然国家目前已经在完善我们的这个系统了)。
  • 这个和我们在应用开发中比较类似:
    • 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。
    • 所以Vuex也使用了单一 状态树来管理应用层级的全部状态。
    • 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。

只定义一个store,公共的状态
在这里插入图片描述
然后在其他组件中直接调用
在这里插入图片描述

2.getters 方法使用

基本使用:

  • 有时候,我们需要从store中获取一些state变异后的状态 ,比如下面的Store中:

    const store = new Vuex.Store({
      state: {
        students: [
          {id:110, name: 'aaa', age: 18},
          {id:111, name: 'bbb', age: 19},
          {id:112, name: 'ccc', age: 20},
          {id:113, name: 'ddd', age: 21}
        ]
      }
    })
    
  • 我们可以在Store中定义getters

    computed: {
          more20stu(){
            return this.$route.state.students.filter(s => s.age >= 20)
          }
        }
    

    或者:

    getters: {
    	more20stu(state){
          return state.students.filter(s => s.age >= 20)
        },
        more20stuLength(state, getters){
          return getters.more20stu.length
    	}
    }
    
  • 然后直接使用在这里插入图片描述

Getters作为参数和传递参数

  • 如果我们已经有了一个获取所有年龄大于20岁学生列表的getters,那么代码可以这样来写

    getters: {
    	more20stu(state){
          return state.students.filter(s => s.age >= 20)
        },
        more20stuLength(state, getters){
          return getters.more20stu.length
    	}
    }
    
  • getters默认是不能传递参数的,如果希望传递参数,那么只能让getters本身返回另一个函数.

    • 比如上面的案例中,我们希望根据ID获取用户的信息
      getters: {
          stuByID(state) {
            return id => {
              return state.students.find(s => s.id === id)
            }
          }
      }
      
3.Mutation状态更新
  • 在通过mutation更新数据的时候,有可能我们希望携带一些额外的参数
    • 参数被称为是mutation的载荷(Payload)

Mutation中的代码:

  mutations: {
	incrementCount(state, count) {
      state.counter += count
    }
  }

App.vue

addCount() {
	this.$store.commit('incrementCount', 2)
}

使用:

<button @click="addCount">+</button>
  • 但是如果参数不是一个呢?
    • 比如我们有很多参数需要传递.

    • 这个时候,我们通常会以对象的形式传递也就是payload是+个对象.

    • 这个时候可以再从对象中取出相关的信息.
      Mutation中的代码:

      mutations: {
      	incrementCount(state, payload) {
            // state.counter += count
            state.counter += payload.count
          }
        }
      

      App.vue

      addCount() {
              this.$store.commit('incrementCount', {count: 2})
            }
      

      另一种方法:

      addCount(count) {
       this.$store.commit('incrementCount', count)
      }
      

      使用:

      <button @click="addCount(5)">+5</button>
      

Mutation提交风格

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

  • Vue还提供了另外一种风格,它是一个包含type属性的对象

    addCount(count) {
            // 第二个参数是payload: 负载
            // 普通的提交风格
            // this.$store.commit('incrementCount', count)
    
            // 特殊的提交风格
            this.$store.commit({
              type: 'incrementCount',
              count
            })
          }
    
  • Mutation中的处理方式是将整个commit的对象作为payload使用,所以代码没有改变,依然如下:

    incrementCount(state, payload) {
          // state.counter += count
          state.counter += payload.count
        }
    

Mutation响应规则

  • Vuex的store中的state是响应式的,当state中的数据发生改变时, Vue组件会自动更新.

  • 这就要求我们必须遵守一些Vuex对应的规则:

    • 提前在store中初始化好所需的属性.
    • 当给state中的对象添加新属性时,使用下面的方式:
      ➢方式一:使用Vue.set(obj, ‘newProp’, 123)
      ➢方式二:用心对象给旧对象重新赋值
  • 我们来看一个例子:

    methods: {
    	updateInfo() {
            his.$store.commit('updateInfo', {height: 1.88})
    }
    
    const store = new Vuex.Store({
      state: {
    	  info: {
    	      name: 'bababa',
    	      age: 46,
    	      height: 2.0
    	    }
      },
    	mutations{
    	
    		updateInfo(state, payload) {
    	      state.info['height'] = payload.heigth
    	}
    })
    

    在这里插入图片描述
    当我们点击更新信息时,界面并没有发生对应改变.

  • 如何才能让它改变呢?

    • 查看下面代码的方式一和方式二
    • 都可以让state中的属性是响应式的.
      mutations{
      	updateInfo(state, payload) {
            state.info.name = 'jajajja'
            // 错误代码:不能在这里进行异步操作
            // setTimeout(() => {
            //   state.info.name = 'jajajja'
            // }, 1000)
            // 下面这个方法也做不到响应式
            // state.info['address'] = '洛杉矶'
            // 用这个:法一
            // Vue.set(state.info, 'address', '洛杉矶')
            // 下面这个方法也做不到响应式
            // delete state.info.age
            // 用这个
            // Vue.delete(state.info, 'age')
            // 法二
            state.info = {...state.info, 'height': payload.height}
          }
      }
      

原因如下:
在这里插入图片描述

Mutation常量类型-概念

  • 我们来考虑下面的问题:

    • 在mutation中,我们定义了很多事件类型(也就是其中的方法名称).
    • 当我们的项目增大时,Vuex管理的状态越来越多,需要更新状态的情况越来越多,那么意味着Mutation中的方法越来越多.
    • 方法过多,使用者需要花费大量的经历去记住这些方法,甚至是多个文件间来回切换查看方法名称,甚至如果不是复制的时候可能还会出现写错的情况.
  • 那怎么办呢?

    • 1.新创建一个js文件
      定义并导出方法名:请添加图片描述
    • 2.在用到的文件导入以及使用
      请添加图片描述
      mutations也用上:
      请添加图片描述

Mutation同步函数

  • 通常情况下,Vuex要求我们Mutation中的方法必须是同步方法.

    • 主要的原因是当我们使用devtools时,可以devtools可以帮助我们捕捉mutation的快照。
    • 但是如果是异步操作,那么devtools将不能很好的追踪这个操作什么时候会被完成.
      请添加图片描述
  • 比如我们之前的代码,当执行更新时,devtools中会有如”下信息:
    在这里插入图片描述

  • 但是,如果Vuex中的代码,我们却已经使用了异步函数请添加图片描述

  • 你会发现state中的info数据一直没有被改变, 因为他无法追踪到。

  • So,通常情况下,不要再mutation中进行异步的操作

4.Action异步操作
  • 我们强调,不要再Mutation中进行异步操作.
    • 但是某些情况,我们确实希望在Vuex中进行一些异步操作, 比如网络请求,必然是异步的.这个时候怎么处理呢?

    • Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.
      Action的基本使用代码如下:

      actions: {
          // context: 上下文
          // aUpdateInfo(context, payload) {
          //   setTimeout(() => {
          //     context.commit('updateInfo')
          //     console.log(payload.message);
          //     payload.success()
          //   }, 1000)
      
            aUpdateInfo(context, payload) {
              return new Promise((resolve, reject) => {
                setTimeout(() => {
                  context.commit('updateInfo')
                  console.log(payload)
                  resolve('11111111')
                }, 1000)
              })
          }
        }
      

      App.vue

      updateInfo() {
              // this.$store.commit('updateInfo')
              // this.$store.dispatch('aUpdateInfo', {
              //   message: '我是携带的信息',
              //   success: () => {
              //     console.log('里面已经完成了');
              //   }
              // })
      
              this.$store
                .dispatch('aUpdateInfo', '我是携带的信息')
                .then(res => {
                  console.log('里面完成了提交');
                  console.log(res);
                })
            }
      

注意:
用的是dispatch
请添加图片描述

通过action帮助mutation进行操作,异步操作也在action里进行

再来一次官方图:
在这里插入图片描述
这回是不是清晰多了?

5.Module
  • Module是模块的意思,为什么在Vuex中我们要使用模块呢?
    • Vue使用单一 状态树,那么也意味着很多状态都会交给Vuex来管理.
    • 当应用变得非常复杂时,store对象就有可能变得相当臃肿.
    • 为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state、mutation、action、getters等
  • 我们按照什么样的方式来组织模块呢?
    • 我们来看下边的代码
      const moduleA = {
        state: {...},
        mutations: {...},
        actions: {...},
        getters: {...}
      }
      
      const moduleB = {
        state: {...},
        mutations: {...},
        actions: {...},
        getters: {...}
      }
      
      const store = new Vuex.Store({
      	modules: {
          a: moduleA
          b: moduleB
        }
      })
      
      store.state.a	// -->moduleA的状态
      store.state.b	// -->moduleB的状态
      

Module局部状态

  • 上面的代码中,我们已经有了整体的组织结构,下面我们来看看具体的局部模块中的代码如何书写.
    • 我们在moduleA中添加state. mutations. getters

    • mutation和getters接收的第一个参数是局部状态对象

      const moduleA = {
        state: {
          name: '我是module里面的a的name'
        },
        mutations: {
          updateName(state, payload) {
            state.name = payload
          }
        },
        getters: {
          fullName(state) {
            return state.name + 'getter修改的内容'
          },
          fullName2(state, getters) {   // getters获取getters里的内容
            return getters.fullName + '22222222'
          }
        }
      }
      
      export default {
          name: 'App',
          methods: {
      	updateName() {
              this.$store.commit("updateName", "我是module里面的a的修改后的name")
            }
       }
      

      使用:

      <h2>=============App内容: modules中的内容===============</h2>
          <h2>{{$store.state.a.name}}</h2>
          <button @click="updateName">修改名字</button>
          <h2>{{$store.getters.fullName}}</h2>
          <h2>{{$store.getters.fullName2}}</h2>
      

注意:
➢虽然,我们的fullName是定义在对象内部的.
➢但是在调用的时候,依然是通过this.$store来直接调用的.

Modules中actions的写法

  • actions的写法呢?接收一个context参数对象
    • 局部状态通过context.state暴露出来,根节点状态则为context.rootState
const moduleA = {
	actions: {
	    aUpdateName(context) {
	      console.log(context);
	      setTimeout(() => {
	        context.commit('updateName', 'zhangsan')
	      }, 1000)
    }
  }
}
  • 如果getters中也需要使用全局的状态,可以接受更多的参数
getters: {
	fullName3(state, getters, rootState) {    // rootState获取根state里面的内容
      return getters.fullName2 + rootState.counter
    }
}

五、项目结构

当我们的Vuex帮助我们管理过多的内容时,好的项目结构可以让我们的代码更加清晰.

我们把除了state的每一个模块都抽出来
在这里插入图片描述

例:
在这里插入图片描述
然后用导入的方式连接:
在这里插入图片描述

ok!!!
Vuex先到这里

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值