【前端Vue】Vuex

1. Vuex介绍和作用

1.1 Vuex 能做什么?

  1. 官方解释:Vuex 是一个专为 Vue.js应用程序开发的 状态管理模式。它采用 集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel调试、状态快照导入导出等高级调试功能。

1.2 状态管理又是什么?

  1. 其实,可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。

  2. 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。

  3. 那么,多个组件就可以共享这个对象中的所有变量属性。

Vue状态管理图例

1.3 难道我们不能自己封装一个对象来管理吗?官方还需要专门出一个插件 Vuex?

看看自己写的
const Store = {
  state: {},
  mutations: function () {

  }

  // ....
};

// 将 Store对象挂载到 Vue 实例
Vue.prototype.Store = Store; // 但是这样挂载的对象不具备响应式 
自己定义一个全局状态管理
  1. 当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。

  2. 如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。

  3. 不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。

1.4 管理些什么状态?

  1. 用户的登录状态、用户名称、头像、地理位置信息等等。

  2. 还有比如商品收藏、购物车物品。

2. Vuex 的安装

  1. 使用 npm install vuex --save命令安装 Vuex为运行时依赖。

3. Vuex 的使用

  1. src/下创建一个 index.js文件,安装Vuex和创建 Vuex.Store对象并导出。
import Vue from 'vue';
import Vuex from 'vuex';

// 安装 Vuex
Vue.use(Vuex);

const store = new Vuex.Store({
  /**
   *
   */
  state: {
    counter: 1000
  },
  /**
   *
   */
  mutations: {},
  /**
   * 有异步操作的时候再actions中进行
   */
  actions: {},
  getters: {}
});

// 导出是方便 在 main.js 中挂载
export default store;
创建Vuex.Store对象并导出
  1. main.js中导入 store对象,并挂载到Vue实例上:
在 `main.js`中导入 `store`对象,并挂载到`Vue`实例上
  1. 此时在全局就可以使用store中的内容了,但是不建议直接操作state中的数据。
<div>
    <h2>{{$store.state.counter}}</h2>
  </div>

3.1 Vuex小案例 - vuex的Mutations

Vuex小案例
  1. 安装vuex为运行时依赖npm install vuex --save
  1. 创建一个 store/inidex.js文件,创建 Vue.Store实例。
import Vue from 'vue';
import Vuex from 'vuex';
// 安装 Vuex
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    counter: 666
  },
  mutations: {
    increment(state) {
      state.counter++;
    },
    decrement(state) {
      state.counter--;
    }
  }
});

// 导出store
export default store;
  1. main.js入口文件中引入store并挂载到 Vue实例上。
在Vue实例中挂载Store对象
  1. 创建一个 HelloVuex组件,获取$store.state.counter数据展示。
<template>
  <h2>
    {{$store.state.counter}}
  </h2>
</template>

<script>
  export default {
    name: "hello-vuex"
  }
</script>

<style scoped>

</style>
  1. App.vue中使用 HelloVuex并使用Store中的mutations改变counter
<template>
  <div id="app">
    <h2>我是App组件</h2>
    <h2>{{$store.state.counter}}</h2>
    <button @click="addition">+</button>
    <button @click="subtraction">-</button>
    <hello-vuex/>
  </div>
</template>

<script>
  import HelloVuex from './components/HelloVuex'
  export default {
    name: 'App',
    components:{
      HelloVuex
    },
    methods: {
      addition() {
        this.$store.commit('increment')
      },
      subtraction() {
        this.$store.commit('decrement')
      }
    }
  }
</script>
<style></style>
操作流程

4. Vuex的几个核心概念

  1. State;

  2. Getters;

  3. Mutations;

  4. Action;

  5. Module;

4.1 State 单一状态树

  1. 将所有的状态信息,保存到一个Store对象中保存起来,而不是多个。

4.2 Getters

  1. getters有点类似computed计算属性。
Getters 进行基本的操作
import Vue from 'vue';
import Vuex from 'vuex';

// 安装Vuex
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    counter: 100,
    students: [
      {name: '张三', age: 23},
      {name: '陆小凤', age: 16},
      {name: '花满楼', age: 15},
      {name: '西门吹雪', age: 30},
    ]
  },
  /**
   * 类似计算属性
   */
  getters: {
    /**
     * 计算 state.counter的平方
     * @param state
     */
    powCounter(state) {
      return state.counter * state.counter
    },
    /**
     * 想筛选年龄大于 20岁的学生的信息
     */
    more20Stu(state) {
      return state.students.filter(stu => stu.age >= 20);
    }
  }
});
export default store;
    <h2> A powCounter : {{$store.getters.powCounter}}</h2>
    <h2> A more20Stu : {{$store.getters.more20Stu}}</h2>
getters作为参数 -> 计算满足条件的学生个数
    /**
     * 想筛选年龄大于 20岁的学生的信息
     */
    more20Stu(state) {
      return state.students.filter(stu => stu.age >= 20);
    },
    /**
     * 获取年龄大于 20 岁学生的个数
     * @param state
     * @param getters
     * @returns {number}
     */
    more20StuLength(state, getters) {
      return getters.more20Stu.length
    
<h2> HelloVuex more20StuLength : {{$store.getters.more20StuLength}}</h2>
Getters 中如果接受外界传递的参数
  1. Getters中接收外界传递的参数可以以返回一个函数的方式进行,在返回的函数中传递参数。
moreAgeStu(state) {
      // 这里返回的是一个函数 在返回的函数中传递参数
      return (age) => state.students.filter(stu => stu.age >= age)
    }
<h2> HelloVuex moreAgeStu : {{$store.getters.moreAgeStu(20)}}</h2>

4.3 Mutations

  1. MutationsVuexStore状态的更新唯一的方式。
mutations 中传递参数
mutations 中传递参数
mutations的提交风格
mutation的提交风格
  1. 值得注意的是此时传递过去的参数是一个对象。
mutations响应规则及原理
  1. Vuexstore中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新。
mutation 响应式的要求
  1. 提前在store中初始化好所需的属性。

  2. 当给state中的对象添加或删除属性时, 使用下面的方式:

Vue.set(state.info , 'address','大雷音寺') // 这样修改是响应式的
Vue.delete(state.info, 'age') // 这也是响应式的
// 或者使用新对象给旧对象赋值
state.info = {name: '王五', age: 25, height: 1.5555, address: '西山区'}
mutations类型常量
mutation类型常量
mutation 同步函数
  1. 通常情况下, Vuex要求我们Mutation中的方法必须是同步方法。主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照。
  1. 比如之前执行更新的代码中,如果使用异步的方式进行对象属性数据的更新,会发现在 devtools中的state无法追踪到数据的变化。所以,通常情况下,不要再 Mutations 中进行异步操作。

// store/index.js
updateInfo(state) {
     // 在 mutations 中进行异步操作
    setTimeout(() => {
      state.info.name = "孙悟空"
    },1000)
   }
// App.vue
updateStoreInfo() {
        this.$store.commit('updateInfo');
      }
在mutations中使用异步方法出现数据变化和devtools不一致

4.4 actions

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

  2. Action中进行操作,Action类似于Mutation, 但是是用来代替Mutation进行异步操作的。

actions的基本使用
  1. context 是什么? context 是和store 对象具有相同方法和属性的对象,也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等,但是注意, 这里它们并不是同一个对象。
在actions中进行异步操作devtools中进行了同步

// store/index.js
actions: {
    aUpdateInfo(context) {
      setTimeout(() => {
          context.commit('updateInfo')
      }, 1000)
    }
  }
// App.vue
updateStoreInfoForActions() {
        this.$store.dispatch('aUpdateInfo');
      }
actions也支持 payload
  1. 在函数中传递 payload
  /**
       * 传递 payload
       */
      updateStoreInfoForActions() {
        this.$store.dispatch('aUpdateInfo', {
          message: '我是payload',
          success: function () {
            console.log('操作成功')
          }
        });
      }
  1. actions中接收参数:
actions: {
    aUpdateInfo(context, payload) {
      setTimeout(() => {
        context.commit('updateInfo')
        console.log(payload.message);
        payload.success()
      }, 1000)
    }
  }
actions 中使用 Promise 处理异步操作
  1. 使用 Promise处理异步请求,将Promise对象返回:
    /**
     * 传递payload参数,使用Promise的方式处理异步请求
     * @param context
     * @param payload
     */
    aUpdateInfo(context, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          context.commit('updateInfo')
          resolve(payload.message + payload.success())
        }, 1000)
      })
    }
  1. 处理Promise对象的then:
      /**
       * 传递 payload
       */
      updateStoreInfoForActions() {
        this.$store.dispatch('aUpdateInfo', {
          message: '我是payload',
          success: function () {
            console.log('操作成功')
          }
        }).then(value => {
          console.log(value);
        })
      }

4.5 modules

  1. Vue 使用单一状态树,那么也意味着很多状态都会交给Vuex来管理。当应用变得非常复杂时,store对象就有可能变得相当臃肿,为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等。
modules的使用方式
Module局部状态
  1. 我们在moduleA中添加state、mutations、getters

  2. mutationsgetters接收的第一个参数是局部状态对象。

  3. 虽然, 我们的doubleCountincrement都是定义在对象内部的,但是在调用的时候, 依然是通过this.$store来直接调用的。

在modules中定义increment和doubleCount
直接调用doubleCount和increment
Actions的写法
  1. 局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
actions中第一个参数context的对象属性
  1. 如果getters中也需要使用全局的状态, 可以接受更多的参数:
如果getters中需要更多的参数可以使用rootState接收,这是第三个参数

5. 项目结构

  1. 重点是 : export import 导入导出。
好的项目结构

推荐阅读更多精彩内容