vue学习记录day10:Vuex

Vuex是什么?

官方解释: Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
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 主要是用户的各种操作:点击、输入等等,会导致状态的改变。
 
写点代码,加深理解:
看下右边的代码效果 , 肯定会实现吧 ?

 

1.安装vuex插件

npm install vuex --save

2.创建store文件夹添加index.js 

import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插件
Vue.use(Vuex)

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

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



3.在main.js中引入

4.使用$store.state.变量名

 

多界面状态管理

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

 

注意:Actions: 做异步操作  Mutations: 做同步操作

 

首先,我们需要在某个地方存放我们的Vuex代码:
这里,我们先创建一个文件夹store,并且在其中创建一个index.js文件
index.js文件中写入如下代码:

使用

 

Vuex核心概念

Vuex有几个比较核心的概念:
  • State
  • Getters
  • Mutation
  • Action
  • Module

 

State单一状态树

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

Getters基本使用

 

Mutation状态更新

Vuex store 状态的更新唯一方式: 提交 Mutation
Mutation 主要包括两部分:
字符串的 事件类型( type
一个 回调函数( handler , 该回调函数的第一个参数就是 state

 

 

Mutation传递参数

 

Mutation提交风格

 
 

Mutation响应规则

Vuex store 中的 state 是响应式的 , state 中的数据发生改变时 , Vue 组件会自动更新 .
这就要求我们必须遵守一些 Vuex 对应的规则 :
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时, 使用下面的方式:
  • 方式一: 使用Vue.set(obj, 'newProp', 123)
  • 方式二: 用心对象给旧对象重新赋值
我们来看一个例子 :
当我们点击更新信息时 , 界面并没有发生对应改变 .
如何才能让它改变呢 ?
查看下面代码的方式一和方式二
都可以让 state 中的属性是响应式的 .

Mutation常量类型 概念

我们来考虑下面的问题 :
mutation , 我们定义了很多事件类型 ( 也就是其中的方法名称 ).
当我们的项目增大时 , Vuex 管理的状态越来越多 , 需要更新状态的情况越来越多 , 那么意味着 Mutation 中的方法越来越多 .
方法过多 , 使用者需要花费大量的经历去记住这些方法 , 甚至是多个文件间来回切换 , 查看方法名称 , 甚至如果不是复制的时候 , 可能还会出现写错的情况 .
如何避免上述的问题呢 ?
在各种 Flux 实现中 , 一种很常见的方案就是使用 常量 替代 Mutation 事件的类型 .
我们可以将这些常量放在一个单独的文件中 , 方便管理以及让整个 app 所有的事件类型一目了然 .
具体怎么做呢 ?
我们可以创建一个文件 : mutation-types.js, 并且在其中定义我们的常量 .
定义常量时 , 我们可以使用 ES2015 中的风格 , 使用一个常量来作为函数的名称 .
 
 
 
 
 
 

Mutation同步函数

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

Action的基本定义

我们强调 , 不要再 Mutation 中进行异步操作 .
但是某些情况 , 我们确实希望在 Vuex 中进行一些异步操作 , 比如网络请求 , 必然是异步的 . 这个时候怎么处理呢 ?
Action 类似于 Mutation, 但是是用来代替 Mutation 进行异步操作的 .
Action 的基本使用代码如下 :
context 是什么 ?
context 是和 store 对象具有相同方法和属性的对象 .
也就是说 , 我们可以通过 context 去进行 commit 相关的操作 , 也可以获取 context.state .
但是注意 , 这里它们并不是同一个对象 , 为什么呢 ? 我们后面学习 Modules 的时候 , 再具体说 .
这样的代码是否多此一举呢 ?
我们定义了 actions, 然后又在 actions 中去进行 commit, 这不是脱裤放屁吗 ?
事实上并不是这样 , 如果在 Vuex 中有异步操作 , 那么我们就可以在 actions 中完成了 .
 
 

 

 

Action的分发

Action返回的Promise

Module

import Vue from 'vue'
import Vuex from 'vuex'
import { INCREMENT } from "./mutations-types";
//1.安装插件
Vue.use(Vuex)


const moduleA = {
  state: {
    name: 'zhangsan'
  },
  mutations: {
    updateName(state, payload){
      state.name = payload
    }
  },
  actions: {
    aUpdateName(context){
      console.log(context.rootGetters)
      setTimeout(
        ()=>{
          context.commit('updateName', 'wangwu')
        },1000)
    }
  },
  getters: {
    fullName(state){
      return state.name + "1111";
    },
    fullName2(state,getters){
      return getters.fullName + '222';
    },
    fullName3(state,getters,rootCounter){
      return getters.fullName2 + rootCounter.counter;
    }
  }
}

//2.创建对象
const store = new Vuex.Store({
  state: {
    counter: 1000,
    students: [
      {id: 110, name: 'why0', age: 18},
      {id: 111, name: 'why1', age: 19},
      {id: 112, name: 'why2', age: 20},
      {id: 113, name: 'why3', age: 21},
      ],
    info: {
      name: 'kobe',
      age: 40,
      height: 1.98
    }
  },
  mutations: {
    //方法
    [INCREMENT](state) {
      state.counter++
    },
    decrement(state) {
      state.counter--
    },
    incrementCount(state,payload){
      //1.普通的提交封装
      // state.counter += count;
      //2.特殊的提交封装
      state.counter += payload.count;
    },
    addStudent(state,stu){
      state.students.push(stu);
    },
    updateInfo(state){
      // state.info.name ='panghl';
      // state.info['address'] ='成都市';  不是响应式的
      //Vue.set(state.info,'address','成都市')

      // delete state.info.age 该方法做不到响应式
      Vue.delete(state.info, 'age')
    }
  },
  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(payload)
        },1000)
      })
    }
  },
  getters: {
    powerCounter(state){
      return state.counter * state.counter;
    },
    more20stu(state){
      return state.students.filter(s=>s.age>=20)
    },
    more20stuLength(state,getters){
      return getters.more20stu.length;
    },
    //getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数
    moreAgeStu(state){
      return function (age) {
        return state.students.filter(s=>s.age>=age)
      }
    }
  },
  modules: {
    a: moduleA
  }
})

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



<template>
  <div id="app">
    <h2>-----------APP内容:modules中的内容-------------</h2>
    <h2>{
   
   {$store.state.a.name}}</h2>
    <button @click="updateName">修改名字</button>
    <button @click="asyncUpdateName">异步修改名字</button>
    <h2>{
   
   {$store.getters.fullName}}</h2>
    <h2>{
   
   {$store.getters.fullName2}}</h2>
    <h2>{
   
   {$store.getters.fullName3}}</h2>

    <h2>-----------APP内容-------------</h2>
    <h2>{
   
   {message}}</h2>
    <h2>{
   
   {$store.state.counter}}</h2>
    <button @click="addition">+</button>
    <button @click="subtraction">-</button>
    <button @click="addCount(5)">+5</button>
    <button @click="addCount(10)">+10</button>
    <button @click="addStudent">+student</button>
    <button @click="updateInfo">updateInfo</button>
    <h2>-----------APP内容:getters相关信息-------------</h2>
    <h2>{
   
   {$store.getters.powerCounter}}</h2>
    <li>{
   
   {$store.getters.more20stu}}</li>
    <li>{
   
   {$store.getters.more20stuLength}}</li>
    <li>{
   
   {$store.getters.moreAgeStu(8)}}</li>

    <h2>---------------APP内容: info对象的内容是否是响应式的----------</h2>
    <h2>{
   
   {$store.state.info}}</h2>

    <h2>---------------Hello Vuex内容----------</h2>
    <hello-vuex></hello-vuex>
  </div>
</template>

<script>
  import HelloVuex from "./components/HelloVuex";
  import {INCREMENT} from './store/mutations-types'
export default {
  name: 'App',
  components: {HelloVuex},
  data(){
    return {
      message: '你好'
    }
  },
  computed: {
  },
  methods: {
    addition(){
      this.$store.commit(INCREMENT)
    },
    subtraction(){
      this.$store.commit('decrement')
    },
    addCount(count){
      //payload: 负载
      //1.普通的提交封装
      //this.$store.commit('incrementCount',count);

      //2.特殊的提交封装
      this.$store.commit({
        type: 'incrementCount',
        count
      })
    },
    addStudent(){
      const stu = {id: 14,name: 'panghl' , age:22}
      this.$store.commit('addStudent',stu);
    },
    updateInfo(){
      // this.$store.commit('updateInfo');
      // this.$store.dispatch("aUpdateInfo",{
      //   message: '我是携带的信息',
      //   success: ()=>{
      //     console.log('里面已经完成了');
      //   }
      //})
      this.$store
        .dispatch('aUpdateInfo', '我是携带的信息')
        .then(res=>{
          console.log('里面完成了提交');
          console.log(res+"then....");
        })
    },
    updateName() {
      this.$store.commit('updateName','lisi')
    },
    asyncUpdateName(){
      this.$store.dispatch('aUpdateName')
    }
  }
}
</script>

<style>

</style>

项目结构

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

猜你喜欢

转载自blog.csdn.net/qq_45441466/article/details/113043723