17-vuex状态管理

6/3/2022

# 一, vuex状态管理

# 1.1, 为什么要使用Vuex

Vue应用程序的状态(state)管理器。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

  • 把“状态”换成“数据”。
  • 如果你的项目中,需要用到在各个子组件中共享数据,则你就需要用到vuex。

# 1.2,示例

设置如下组件:

  • 一个vue实例,充当根组件
  • 子组件AddNumber
  • 子组件SubNumber

希望在两个子组件AddNumber和subNumer之间共同维护和使用数据项:counter

  • 在vue实例中:显示 counter的值
  • 在子组件AddNumber中:显示 counter的值;添加counter的值
  • 在子组件subNumber中:显示 counter的值;减少counter的值
  • 由于上面的需要,我们需要在三个地方共同使用同一数据项counter,所以我们需要用到 vuex

第一步:先引入vue.js,再引入vuex.js

 <!-- 引入Vue.js -->
 <script src="./libs/vue.js"></script>
 <!-- 引入Vuex.js -->
 <script src="./libs/vuex.js"></script>
1
2
3
4

第二步:实例化vuex中的store对象

  • 实例化一个对象,就是通过new的方式去创建一个对象
// 创建一个仓库  需要传入一个配置对象
let store = new Vuex.Store({
    // state是状态  是集中管理的状态
    // 状态是响应式的
    // 组件中的data中的状态也是状态式
    state: {
        counter: 0
    },
    // 修改状态的唯一途径
    mutations: {
        // 每一个mutations就是一个函数
        // 第1个参数是仓库中的state
        // payload 是commit mutation时,传递的参数
        add(state, payload) {
            // state.counter++;
            state.counter += payload
        },
        sub(state) {
            state.counter--;
        }
    },
    // 放异步代码,如果有异步操作,代码需要写在actions中
    actions: {

    },
    // 类似于组件中的计算属性
    // 根据已有状态计算出一个新的状态
    getters: {

    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

第三步:注入到Vue实例中

 let vm = new Vue({
     el: "#app",
     store, // vuex也是一个插件,需要挂载到根组件上,就意味着,它的子子孙孙,都可以使用这个仓库了
     components: {
         AddCounter,
         SubCounter
     },
     data() {
         return {

         }
     }
 });
1
2
3
4
5
6
7
8
9
10
11
12
13

第四步:定义两个组件并在App中使用之

 let AddCounter = Vue.extend({
     template: `
        <div>
            <p>我是addcounter组件</p>
            <p>在addcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
            <button @click="add">加1</button>
        </div>
    `,
     methods: {
         add() {
             // 这样是粗暴地修改状态 极力不推荐
             // 如果其它组件也这样修改状态,状态不好追踪了
             // this.$store.state.counter++;

             // 需要commit一个mutation,通过mutation去修改状态
             this.$store.commit("add", 100)
         }
     }
 })
 let SubCounter = Vue.extend({
     template: `
        <div>
            <p>我是subcounter组件</p>
            <p>在subcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
            <button @click="sub">减1</button>
        </div>
    `,
     methods: {
         sub() {
             // this.$store.state.counter--;

             this.$store.commit("sub")
         }
     }
 })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

第五步:使用store中的数据

  • 一旦你在vue的实例中注入了store,则在所有的子组件及 vue的实例中,你都可以通过:this.$store.state. 数据名 去获取数据
  • 类似于我们把router注入到vue实例中,我们就可以通过this.$router 和 this.$route操作路由。
 <p>在addcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
 <p>在subcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
1
2
  • 可以通过:this.$store.state. 数据名 = 值 去修改数据,但是,vuex反对这么做

你可以有两种方法去使用数据

  • 获取:this.$store.state. 数据名
  • 修改:在组件内部通过this.$commit方法触发事件,执行mutations当中的对应的方法

# 二, 在vue脚手架中使用vuex

设置如下组件:

  • 一个vue实例,充当根组件
  • 子组件AddNumber。
  • 子组件subNumber。

希望在两个子组件AddNumber和subNumer之间共同维护和使用数据项:counter

  • 在vue实例中:显示 counter的值
  • 在子组件AddNumber中:显示 counter的值;添加counter的值
  • 在子组件subNumber中:显示 counter的值;减少counter的值

# 三, vuex的四个概念

  • state: 放数据。在组件之间共享
  • mutations: 修改数据 修改数据的唯一途径
  • getters: 从state中的数据中,取出一部分来,依据数据项产生新的结果。类似于vue实例中的computed(计算属性)。
  • actions: 在对数据实现异步操作时,要用的

# 3.1, store

  • 每一个 Vuex 应用的核心就是 store(仓库)
  • store是一个容器,包含着vue应用的状态(state)

两大特点

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
  • 你不能直接更改 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化

在组件中应该如何去获取保存在vuex. 中的数据

  • 方法一:this.$store.state.xxx
  • 方法二:变通一下,在组件内部用一个计算属性来获取数据
  • 方法三:mapState

mapState

  • this.$store.state.count写起来比较麻烦。在vuex中提供一个便捷的写法:mapState。
  • 它的作用:是把写在vuex.store.state中的数据直接映射到组件中计算属性中。

# 3.2, getters

有时希望在state中的数据基础上,派生出一些其它的数据,此时可以使用getters

在组件中使用getters

  • 方法一:this.$store.getters.failedNumber
  • 方法二 :mapGetters
  • 与mapState类似,它的作用也是用来帮助我们去简化代码。我们如果直接定义一个计算属性也是可以的。

# 3.3, mutations

作用是:它是唯一的用来修改数据的工具

在组件中使用的方式

  • 方法一:this.$store.commit
  • 方法二:mapMutations

# 3.4, actions

在mutations中,如果操作是异步的,在控制台中并不能追踪到

  • 可以把异步操作写在actions中
  • 虽然把异步操作写在actions中,但是改变状态的唯一方式是不变,还是通过mutations

action与mutations的区别

  • actions 提交的是 mutations,而不是直接变更状态。
  • actions 可以包含任意异步操作。
  • action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,可以通过context.commit提交,也可以通过context.state获取state。

在组件中使用actions

  • 方法一: this.$store.dispatch(“actions名”)
  • 方法二:mapActions

# 四, Vuex版本的TodoMVC

目标

# 4.1, 分析

状态:

  • 准备两个数据项。放在vuex中,供组件去使用
  • todos:[{text:"学习vuex, So easy~", done:false}, {text:"学习React, So easy~", done:false}]
  • visibilty:,筛选的条件 :all completed active

功能:

  • 显示todos数据
  • 添加一条todos数据
  • 删除todos数据
  • 修改某一项的状态:完成,没有完成之间切换
  • 批量修改状态: 全部完成,全部未完成
  • 统计没有完成的数量
  • 批量删除已经完成的
  • 三种状态的筛选
  • 编辑todos的内容
  • 本地存储

组件:

  • 在脚手架工具的基础上,额外设置两个组件

# 4.2, 准备静态资源

第一步:准备静态资源,如下:

第二步:把App.vue中的template的内容用todo.html的内容来代替

第三步:在main.js中引入对应的css

# 4.3, 创建并引入仓库

在项目中安装vuex,如下:

  • npm i vuex@3.6.2

创建仓库,如下:

在main.js中去引入

# 4.4, 拆分组件(显示todo数据)

创建ListTodo,如下:

在app.vue组件中,使用Listtodo组件,如下:

把仓库中的todo数据,映射到app.vue组件中,如下:

有多个todo, 就循环出多少个ListTodo组件,如下:

通App.vue这个父组件给ListTodo这个子组件进行数据传递,如下:

在子组件中,设置props接收之,如下:

修改仓库中的状态,如下:

效果如下:

# 4.5, 添加一条todos数据(AddTodo组件)

抽离AddTodo组件,如下:

在app.vue中使用,如下:

效果如下:

下面就要具体去实现添加一条todo的功能

  • 本质上需去vuex.store.state.todos中加一条记录,这里就是涉及修改vuex中的数据,必须要用mutations
  • 所以,我们应该先去vuex中去创建好一个mutations,如下:

把这个方法直接映射到addTodo组件的methods中,如下:

# 4.6, 删除一条todos数据

需求:

删除操作也是要去修改数据,则对应的也要建立一个mutations。如下:

这个删除操作是在listTodo组件中完成的。在 listTodo组件中给删除的按钮绑定事件,如下:

# 4.7, 修改某一项的状态:完成,没有完成之间切换

说白了,就是修改vuex中每个todo的状态(done),如下:

这个操作,本质就是要去修改某一个todo项的done属性。所以也需要在mutations中去写好对应的方法。如下:

在ListTodo,映射成方法,这个工作是在ListTodo组件完成的,我们去给按钮加change事件。如下:

浏览器测试之,如下:

# 4.8, 批量修改状态: 全部完成,全部未完成

首先,我们要去判断当前情况下,是否所有的任务已经全部完成。如果全部完成,则它应该要处于check状态。现在,我们需要在原始数据的基础,加工得到一个新数据:是否全部完成。所以,应该要在vuex.store中的加一个getters,如下:

在app.vue中使用之,如下:

浏览器测试之,如下:

现在只是实现了:根据是否全部完成来设置input的checked。下面,我们还要给它加change事件:

  1. 如果当前是处于checked ,则点击事件发生,要全部改成done:false
  2. 如果当前是没有选中,则点击事件发生,要全部改成done:true

上面的操作,也是要修改vuex中仓库的数据,则对应地要去建立mutations,如下:

在app.vue使用之,如下:

使用toggleAllTodo,如下:

浏览器测试之,如下:

# 4.9, 统计没有完成的数量

统计没有完成的数量,说白了,就是数一数,todos中todo的done是false个数,不需要对数据进行修改,只是在原始数据的基础上,加工得到新数据。去vuex.store中设置一个 Getters,如下:

把它map到app.vue组件中,成为一个计算属性。如下:

使用之,如下:

测试之,如下:

# 4.10, 批量删除已经完成的

这个按钮,只有在“完成了数量>0”的情况下才可见。

  • todos.length是所有任务的数量
  • unDoneNumber: 还没有完成的数量
  • 当Todos.length >unDoneNumber 说明,有一部分是已经完成了的。此时应该要显示这个按钮

下面,接给它添加点击事件,点击之后,要把已经完成的任务从整个的todos当中删除掉。由于这里涉及修改数据 ,我们需要去建立 一个mutations。如下:

下面,把这个功能映射到app.vue组件的methods 中。如下:

给按钮加点击事件,调用这个方法,如下:

浏览器测试之,如下:

# 4.11, 三种状态的筛选

这个操作不会更改数据,只是在原数据的基础上进行筛选,得到新数据。所以,我们去创建getters,如下:

每次点击不同的按钮,就相当于是要去修改visibility,所以,我们要去设置一个mutations,如下:

把上面的changeVisibility映射到app.vue组件的方法中去。如下:

使用之,如下:

浏览器测试之,如下:

最重要的一点:现在的数据的来源不是整个的todos,而是根据当前条件过滤的数据,如下:

使用之,如下:

浏览器测试之,如下:

点击样式进行切换,如下:

浏览器测试之,如下:

# 4.12, 编辑todos的内容

当我们在todo的内容上双击时,则会处于编辑状态:

开启编辑状态:加一个editing这个类.

  • 在listtodo中,设置双击加一个类的效果
  • 给listTodo这个组件加一个isEdit数据项,用它来表示当前是否处于编辑状态

并不是说所有的状态都放在vuex中比较好,如果一个状态只有在一个组件中使用,好么不需要放到vuex中,我们直接把isEdit状态放到组件中,如下:

对文件框和整个的li,它们在编辑状态会有不同的样式:

在浏览器中测试之,如下:

当双击todo时,需要启动编辑,如下:

对输入框来说,初始值就是当前的todo的text,如下:

测试之,如下:

当失去焦点,或者是回车时,都应该结束编辑

但,结束编辑时,应该去修改数据。我们应该定义 一个mutations去处理这种情况。如下:

下面,我们把这个mutations映射到listTodo组件的methods中去。如下:

修改输入框的代码,如下:

修改finishEdit方法,如下:

浏览器测试之,如下:

# 4.13, 本地存储

新建一个模块:

当数据变发生变动,就需要存储数据,在vuex中改变数据的唯一途径就是mutation,所以,只要触发mutaion,说明就要修改数据了,所以说,在每 个mutation中,就需要保存数据了,在store/index.js中引入这个模块,如下:

浏览器测试之,如下:

需要在localStorage中取数据,如下:

浏览器测试之,如下:

Last Updated: 12/25/2022, 10:02:14 PM