Vuex 介绍 / 实现 ToDoLists

目录

一. Vuex 概述

二. Vuex 安装及使用

三. Vuex 核心部分

1. State(存放组件公共数据)

2. Mutation(触发同步任务,修改 State)

3. Action(触发异步任务,不能修改 State)

4. Getter(Vuex 中的 computed)

四. 通过 Vuex 实现 ToDoLists


一. Vuex 概述

1. 除了 Vuex 之外,组件之间共享数据的方式

  • 父向子传值:v-bind 属性绑定
  • 子向父传值:v-on 事件绑定
  • 兄弟组件之间共享数据: EventBus
  1. $on 接收数据的那个组件
  2. $emit 发送数据的那个组件

2. Vuex 概念及作用

  • Vuex 概念:
  • Vuex 是实现组件全局状态(数据)管理的一种机制,实现组件数据共享

  • Vuex 作用:
  1. 集中管理共享的数据,易于开发和后期维护
  2. 高效地实现组件之间的数据共享,提高开发效率
  3. 存储在 Vuex 中的数据都是 响应式的,实时保持数据与页面的同步
  • 注意:只有组件共享的数据,才存储到 Vuex 中;组件的私有数据,依旧存储在组件 自身的 data 中

二. Vuex 安装及使用

  • 安装 Vuex:npm install vuex --save

  • 使用 Vuex:导入 Vuex依赖,创建 Store对象,将 store 对象挂载到 vue 实例中
import Vuex from 'vuex'
Vue.use(Vuex)

// 创建 store 对象
const store = new Vuex.Store({
 state: { count: 0 } // state 存放全局共享的数据
})

new Vue({
 el: '#app',
 render: h => h(app),
 router,
 store // 将 store 对象挂载到 vue 实例中
})

三. Vuex 核心部分

  • Vuex 中的核心概念如下:State Mutation Action Getter

1. State(存放组件公共数据)

  • State 用于存放公共数据

  • 访问 State 中数据的两种方式:
  1. this.$store.state.全局数据名称
  2. 从 Vuex 中,按需导入 mapState 函数,并在 computed 中使用
// 按需导入 mapState 函数
import { mapState } from 'vuex'
// 将全局数据,映射为当前组件的计算属性
computed: {
 ...mapState(['count'])
}

2. Mutation(触发同步任务,修改 State)

  • Mutation 用于 触发同步任务修改 State 数据
  • 注意:
  1. 只能通过 Mutation 变更 Store 数据,不可以直接操作 Store 中的数据
  2. Mutation 处理同步任务,异步任务只能通过 Action 触发

  • 触发 Mutation 的两种方式:
  1. this.$store.commit()
  2. 从 Vuex 中,按需导入 mapMutations 函数,并在 methods 中使用
  // 在 store 中,定义 mutation
  const store = new Vuex.Store({
    state: {
      count: 0
    },
    mutations: {
      // 变更 state 状态
      add(state) {
        state.count++ 
      },
      // 传递参数的 变更 state 状态
      addN(state, step) { 
        state.count += step
      },
    },
  })

  // 组件 methods 内,触发 mutation
  methods: {
    handle1() {
      // 不带参数,触发 mutation
      this.$store.commit('add')
      // 带参数,触发 mutation
      this.$store.commit('addN', 3)
    }
  }

  • 从 Vuex 中按需导入 mapMutations 函数 示例如下:
// 按需导入 mapMutations 函数
import { mapMutations } from 'vuex'

// 将指定的 mutations 函数,映射为当前组件的 methods 函数
methods: {
 ...mapMutations(['add', 'addN'])
}

3. Action(触发异步任务,不能修改 State)

  • Action 用于 触发异步任务,不能修改 State 数据
  • 注意:
  • 只能通过 Mutation 变更 Store 数据,Action 不能变更 store 数据,但是可以在 Action中,触发 Mutation,间接变更数据
  • 如果通过异步操作变更数据,必须通过 Action,不能使用 Mutation

  • 触发 Action 的两种方式:
  1. this.$store.dispatch()
  2. 从 Vuex 中,按需导入 mapActions 函数,并在 methods 中使用
  const store = new Vuex.Store({
    // 同步任务
    mutations: { },
    // 异步任务
    actions: { 
      // 不带参数
      addAsync(context) {
        setTimeout(() => {
          // 内部调用 mutations 间接修改 state数据
          context.commit('add') 
        }, 1000)
      },

      // 带参数
      addNAsync(context, step) {
        setTimeout(() => {
          context.commit('addN', step)
        }, 1000)
      }
    }
  })

  // 在组件 methods 中触发 actions 事件
  methods: {
    handle() {
      // 不带参数
      this.$store.dispatch('addAsync')
      // 带参数
      this.$store.dispatch('addNAsync', 5)
    }
  }

  • 从 vuex 中按需导入 mapActions 函数 示例如下:
import { mapActions } from 'vuex'

methods: {
 ...mapActions(['addASync', 'addNASync'])
}

4. Getter(Vuex 中的 computed)

  • Getter 用于 加工 Store 中的数据,形成新结构数据,类似于 Vue 中的 computed
  • 注意:
  • Store 中数据发生变化,Getter 的数据也会跟着变化

  • 触发 Getter 的两种方式:
  1. this.$store.getters.名称
  2. 从 Vuex 中,按需导入 mapGetters 函数,并在 computed 中使用

  const store = new Vuex.Store({
    state: {
      count: 0,
    },
    getters: {
      showNum: (state) => {
        return "当前最新的数量是【" + state.count + "】";
      },
    },
  });
import { mapGetters } from 'vuex'

computed: {
 ...mapGetters(['showNum'])
}

四. 通过 Vuex 实现 ToDoLists

  import Vue from "vue";
  import Vuex from "vuex";
  import axios from "axios";

  Vue.use(Vuex);

  export default new Vuex.Store({
    // 静态数据
    state: {
      // 任务列表
      list: [],
      // 输入框内容
      inputValue: "",
      // 下一个Id
      nextId: 5,
      // 视图关键字
      viewKey: "all",
    },
    // 修改 state 静态数据
    mutations: {
      // 初始化任务列表
      // 接受 action 中异步获取的表格数据,并更改 state
      initList(state, list) {
        state.list = list;
      },
      // 更改输入框的值
      setInputValue(state, val) {
        state.inputValue = val;
      },
      // 添加列表项
      addItem(state) {
        // 设置一个 类似于 list 中的数据结构对象
        const obj = {
          id: state.nextId, // 下一个id
          info: state.inputValue.trim(), // 输入框中的值(列表项内容)
          done: false, // 是否完成,默认未完成
        };
        // 向列表中添加列表项
        state.list.push(obj);
        // 新id + 1,防止冲突
        state.nextId++;
        // 添加列表之后,记得清空输入框的值
        state.inputValue = "";
      },
      // 根据Id,删除列表项
      removeItem(state, id) {
        // 根据传入的id,查询列表中对应的列表项
        const i = state.list.findIndex((item) => item.id === id);
        // 如果找到了该项,就根据索引,删除对应的列表项
        if (i !== -1) {
          state.list.splice(i, 1);
        }
      },
      // 修改列表项的选中状态
      changeStatus(state, listInfo) {
        // 根据传入的列表项id,查询列表中对应的列表项
        const i = state.list.findIndex((item) => item.id === listInfo.id);
        // 如果找到了该项,就根据索引,修改对应的列表项状态
        if (i !== -1) {
          state.list[i].done = listInfo.status;
        }
      },
      // 清除已完成的任务
      cleanDone(state) {
        // 遍历列表项,将列表项的每一项的完成状态都写为 false
        state.list = state.list.filter((item) => item.done === false);
      },
      // 修改视图的关键字
      changeViewKey(state, key) {
        state.viewKey = key;
      },
    },
    // 触发异步任务,通过 mutations 间接修改 state 数据
    actions: {
      // 异步获取 list 列表(这里通过 axios 模拟)
      getList(context) {
        axios.get("./list.json").then(({ data }) => {
          // console.log(data);
          // 在 ations 中更改 state 的数据,要借助 matutions
          context.commit("initList", data);
        });
      },
    },
    // vuex 中的计算属性(computed)
    getters: {
      // 统计未完成的任务的条数
      unDoneLength(state) {
        return state.list.filter((item) => item.done === false).length;
      },
      // 按需展示列表
      infolist(state) {
        // 点击全部/已做/未做按钮之后 viewKey 发生更改,并展示不同数据
        if (state.viewKey === "all") {
          return state.list;
        } else if (state.viewKey === "undone") {
          return state.list.filter((item) => !item.done);
        } else if (state.viewKey === "done") {
          return state.list.filter((item) => item.done);
        } else {
          return state.list;
        }
      },
    },
  });

  • 在 Vue 中使用 Vuex(此处用了 ant-design 组件库):
<template>
  <div id="app">
    <a-input
      placeholder="请输入任务"
      class="my_ipt"
      :value="inputValue"
      @change="handleInputChange"
    />
    <a-button type="primary" @click="addItemToList">添加事项</a-button>

    <a-list bordered :dataSource="infolist" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框 -->
        <a-checkbox
          :checked="item.done"
          @change="(e) => {cbStatusChanged(e, item.id)}"
          >{{ item.info }}</a-checkbox
        >
        <!-- 删除链接 -->
        <a slot="actions" @click="removeItemById(item.id)">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>{{ unDoneLength }}条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button
            :type="viewKey === 'all' ? 'primary' : 'default'"
            @click="changeList('all')"
            >全部</a-button
          >
          <a-button
            :type="viewKey === 'undone' ? 'primary' : 'default'"
            @click="changeList('undone')"
            >未完成</a-button
          >
          <a-button
            :type="viewKey === 'done' ? 'primary' : 'default'"
            @click="changeList('done')"
            >已完成</a-button
          >
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a @click="clean">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
  import { mapState, mapGetters } from "vuex";

  export default {
    name: "app",
    data() {
      return {};
    },
    created() {
      // 获取列表数据
      this.$store.dispatch("getList");
    },
    computed: {
      // ...mapState(['list']),
      ...mapState(["inputValue", "viewKey"]),
      ...mapGetters(["unDoneLength", "infolist"]),
    },
    methods: {
      // 监听文本框内容变化
      handleInputChange(e) {
        // e.target.value:当前文本框的输入值
        this.$store.commit("setInputValue", e.target.value);
      },
      // 向列表中新增 item 项
      addItemToList() {
        if (this.inputValue.trim().length <= 0) {
          return this.$message.warning("文本框内容不能为空!");
        }
        this.$store.commit("addItem");
      },
      // 很据Id,删除对应的任务事项
      removeItemById(id) {
        // console.log(id)
        this.$store.commit("removeItem", id);
      },
      // 监听复选框选中状态变化的事件
      cbStatusChanged(e, id) {
        // 通过 e.target.checked 可以接受到最新的选中状态
        // console.log(e.target.checked)
        // console.log(id)
        const param = {
          id: id,
          status: e.target.checked,
        };
        this.$store.commit("changeStatus", param);
      },
      // 清除已完成的任务
      clean() {
        this.$store.commit("cleanDone");
      },
      // 修改页面上展示的列表数据
      changeList(key) {
        // console.log(key)
        this.$store.commit("changeViewKey", key);
      },
    },
  };
</script>

<style scoped>
  #app {
    padding: 10px;
    margin-left: 50%;
    transform: translateX(-50%);
  }

  .my_ipt {
    width: 500px;
    margin-right: 10px;
  }

  .dt_list {
    width: 500px;
    margin-top: 10px;
  }

  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
</style>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值