一个案例带你弄懂Vuex |基础案例

485 阅读5分钟

前言

之前的文章介绍了Vuex的核心概念,现在我们用一个小的demo来练习巩固一下吧~

案例效果案例效果

案例功能

1.增加便签事项

2.删除事项

3.剩余事项统计

4.批量清除

5.根据状态切换事项列表

所需技能

1.vue

2.Vuex

3.Axios

4.ant-design-vue UI库 ant-design-vue 官网

目录结构

项目前期准备

  • 新建项目,安装依赖,这个项目是vue-cli写的可以使用 vue-ui 可视化搭建项目。
  • 安装Vuex
Npm install vuex --save
  • 安装依赖
npm install axios --save
npm i --save ant-design-vue
  • 打开main.js引入安装的ui 库 这里可以按需引入,也可以完整引入,我采用了完整引入
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css';
Vue.use(Antd)
  • 5.页面搭建 打开任意一个.vue文件 这里接直接上代码了,文档中都有
<template>
  <div id="app">
    <!-- 输入框 -->
    <a-input placeholder="Basic usage" class="my_ipt" />
    <!-- 添加按钮 -->
    <a-button type="primary">
      添加事项
    </a-button>
    <!-- 列表容器 -->
    <a-list bordered :data-source="list" class="my_list">
      <!-- 列表项 -->
      <a-list-item slot="renderItem" slot-scope="item">
        <a-checkbox>{{ item.info }} </a-checkbox>
        <a slot="actions">删除</a>
      </a-list-item>
      <!-- 底部 -->
      <div slot="footer" class="footer">
        <!-- 剩余代办 -->
        <span class="dataNum">3条剩余</span>
        <!-- 状态切换按钮 -->
        <div class="btns">
          <a-button type="primary">
            全部
          </a-button>
          <a-button>
            未完成
          </a-button>
          <a-button>
            已完成
          </a-button>
        </div>
        <!-- 清除已完成 -->
        <a class="clearBtn">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>
  • 接下来在src文件夹下新建index.json文件,写入数据
[
  {
    "id": 0,
    "info": "Racing car sprays burning fuel into crowd.",
    "done": false
  },
  {
    "id": 1,
    "info": "Japanese princess to wed commoner.",
    "done": true
  },
  {
    "id": 2,
    "info": "Australian walks 100km after outback crash.",
    "done": false
  },
  {
    "id": 3,
    "info": "Man charged over missing wedding girl.",
    "done": true
  },
  {
    "id": 4,
    "info": "Los Angeles battles huge wildfires.",
    "done": false
  }
]

功能实现

数据加载与绑定

  • 将数据显示在页面中

因为我们要用axios加载数据,所以这是一个异步请求,我们要将方法写在action中

  • 找到 store 文件夹下的 index.js 导入 axios
import axios from "axios";
  • 在state定义存取数据的数组
state: {
    //初始化的数据
    list: [],
  },
  • 在action中定义请求方法
actions: {
    getList(context) {
      axios.get("/index.json").then((res) => {
        Console.log(res.data)
      });
    },
  },
  • #####定义完毕后回到.vue文件使用 $store.dispatch.方法名 触发此方法 ,因为是 axios 请求所以要在 created 中触发
created(){
    this.$store.dispatch('getList')
  },
  • 打开浏览器可以看到请求数据请求成功

运行结果

  • 接下来要做的事就是我们数据拿回来了 怎么赋值给state中的list数组呢,之前说过,想要改变 stat e中的数据就必要通过 mutation,那么就回到 store 文件夹下的 index.js 文件的 action 方法中触发一个 mutation,使它把值赋给我们需要的list数组。
actions: {
    getList(context) {
      axios.get("/index.json").then((res) => {
        context.commit("showData", res.data);
      });
    },
  },
  • 定义所触发的mutation将值付给state中的list
 mutations: {
    //初始化数据
    showData(state, list) {
      state.list = list;
	},
 }
  • 此时list数组已经有值,回到.vue文件中就可以用按需导入使用数据了,需要注意的是 mapState 需要放在计算属性中。
import { mapState} from 'vuex'
computed:{
    ...mapState(['list']),
  },
  • 增加便签事项

  • 实现思路:获取input中输入的值,存到store中,点击添加事项按钮,将获取到的input中的值存到列表中
  • 双向绑定input的值:在store下的index.js中定义iptValue,回到.vue文件中在computed属性中取出,并绑定到input中
//index.js
state: {
    //初始化的数据
    list: [],
    //获取input框的值
    iptValue: "",
  },
//.vue文件中
computed:{
    ...mapState(['list','iptValue']),
  },
//.vue文件中
<a-input placeholder="Basic usage" :value="iptValue" class="my_ipt" @change="changeIptValue"/>
  • 在input绑定事件(事件说明文档中都有),接着在methods中定义方法,获取到输入的值,将值传递过去,再回到index.js中赋值给定义的iptValue,完成保存。
//.vue文件中
<a-input placeholder="Basic usage" :value="iptValue" class="my_ipt" @change="changeIptValue"/>
 //.vue文件中
 //获取input框的值,并触发mutation
 changeIptValue(e){
      this.$store.commit('setIptValue',e.target.value)
    },
//index.js
//获取input框的值,赋值给state.iptValue
    setIptValue(state, val) {
      state.iptValue = val;
    },
  • input中的值获取成功后,现在就是要将添加的事项,显示在list中,所以在添加事项的按钮上绑定点击事件,触发一个mutation,并回到index.js中定义这个mutation
// .vue文件中
//添加列表项
addItemTodolist(){
//输入的值为空,弹出提示
      if(this.iptValue.trim().length===0){
        return this.$message.warning('文本框的内容不能为空!')
      }
      //触发mutation
      this.$store.commit('addItemTodolist')
},
//index.js
 //添加列表项
    addItemTodolist(state) {
      const obj = {
        id: state.nextId,
        info: state.iptValue,
        donefalse,
      };
//将对象存到数组中
      state.list.push(obj);
      state.nextId++;
//清空input的值
      state.iptValue = "";
    },
  • 删除事项

  • 找到删除按钮,绑定点击事件,获取被点击事项的id值,传给新定义mutation中
//.vue文件中
<a slot="actions" @click="delItemTodolist(item.id)">删除</a>
//.vue文件中
//删除列表项
    delItemTodolist(id){
    //触发mutation
      this.$store.commit('delItemTodoList',id)
},
  • 在mutation中定义方法,使用findIndex()找到被选事项。接着使用splice方法,移出被选事项
//index.js中
//删除列表项
 delItemTodoList(state, id) {
 //找到要被删除的索引
      const i = state.list.findIndex((v) => v.id === id);
      state.list.splice(i, 1);
    },
  • 剩余事项统计

  • 我们通过切换CheckBox来设置事项的完成状态,所以统计剩余事项首先要获取一下CheckBox的值,并将值存起来。
<!-- .vue文件中 -->
 <a-checkbox :checked='item.done' @change="(e) => { changeckStatus(e,item.id) }">{{ item.info }} </a-checkbox>
//.vue文件中
//复选框状态更改
    changeckStatus(e,id){
    //传递参数
      const params={
        status:e.target.checked,
        id:id
      }
      //触发mutation
      this.$store.commit('changeCkStatus',params)
    },
//index.js中
//复选框状态更改
    changeCkStatus(state, params) {
    //找到被点击事项
      const index = state.list.findIndex((v) => v.id === params.id);
      if (index !== -1) {
      //将传递过来的值进行赋值
        state.list[index].done = params.status;
      }
    },
  • 接着在getters中,过滤出未完成事项的数组,获取长度,回到.vue文件在computed中声明,并绑定。
//.vue文件中
//按需导入mapGetters
import { mapState, mapGetters} from 'vuex'

//声明定义的getter
 computed:{
    ...mapState(['list','iptValue']),
    ...mapGetters(['unDoneLength'])
  },

//在页面中绑定
 <span class="dataNum">{{unDoneLength}}条剩余</span> 
  • 根据状态切换事项列表

  • 点击按钮切换按钮样式:首先给每个按钮设置一个 key 值,然后绑定点击事件,触发一个 mutation 将 key 值传到 state 中保存, 在 index.js 的 state 中定义一个 btnStatus,接着定义 mutation 的方法,再回到 .vue 文件,通过 mapStat e导入 btnStatus,做一个三元表达式的判断,如果等于对应的 key type值就等于 'primary'(选中状态)否则等于默认值 'default'。
//.vue文件中
  <div class="btns">
          <a-button :type="btnStatus === 'all' ? 'primary' : 'default'" @click="changeBtnStatus('all')">
          全部
        </a-button>
        <a-button :type="btnStatus === 'undone' ? 'primary' : 'default'"  @click="changeBtnStatus('undone')">
          未完成
        </a-button>
        <a-button :type="btnStatus === 'done' ? 'primary' : 'default'"  @click="changeBtnStatus('done')">
          已完成
        </a-button>
        </div>
//切换按钮状态方法
changeBtnStatus(key){
     console.log(key);
     //触发mutation 并传值
     this.$store.commit('changeBtnStatus',key)
   },        
//index.js中
//将值赋值给state中的btnStatus
    changeBtnStatus(state, key) {
      state.btnStatus = key;
    },

//.vue文件中
computed:{
	//按需导入btnStatus
    ...mapState(['list','iptValue','btnStatus']),
    ...mapGetters(['unDoneLength'])
  },
  • 按钮样式做完了,要做的就是点击对应按钮显示对应数据了,可以通过getter实现数据过滤,然后回到.vue文件导入getter ,重新绑定:data-source

注:重新绑定是因为数组通过我们定义的方法是动态改变的,而我们之前绑定的数组是不会改变的。

//index.js中
changeItemList(state) {

      if (state.btnStatus === "all") {
        return state.list;
      } 
      else if (state.btnStatus === "undone") {
        return state.list.filter((v) => v.done === false);
      } 
      else if (state.btnStatus === "done") {
        return state.list.filter((v) => v.done === true);
      } 
      else {
        return state.list;
      }
    },
//.vue文件中
 computed:{
    ...mapState(['list','iptValue','btnStatus']),
    //按需导入changeItemList
    ...mapGetters(['unDoneLength','changeItemList'])
  },
<!-- .vue文件中 -->
<!-- .绑定getter -->
<a-list bordered :data-source="changeItemList" class="my_list">
  • 批量清除

  • 绑定事件
<!-- .vue文件中 -->
 <a class="clearBtn" @click="clearDone">清除已完成</a>
//.vue文件中
 //清除已完成
    clearDone(){
    //触发mutation
      this.$store.commit('clearDone')
    }
//index.js中
 clearDone(state){
 //过滤出未完成的数据
      state.list = state.list.filter(v => v.done!==true)
    }
  • 完整代码

//.vue文件中
<template>
  <div id="app">
    <!-- 输入框 -->
    <a-input placeholder="Basic usage" :value="iptValue" class="my_ipt"  @change="changeIptValue"/>
    <!-- 添加按钮 -->
    <a-button type="primary" @click="addItemTodolist()">
      添加事项
    </a-button>

    <a-list bordered :data-source="changeItemList" class="my_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <a-checkbox :checked='item.done' @change="(e) => { changeckStatus(e,item.id) }">{{ item.info }} </a-checkbox>
        <a slot="actions" @click="delItemTodolist(item.id)">删除</a>
      </a-list-item>
      <div slot="footer" class="footer">
        <span class="dataNum">{{unDoneLength}}条剩余</span> 
        <div class="btns">
          <a-button :type="btnStatus === 'all' ? 'primary' : 'default'" @click="changeBtnStatus('all')">
          全部
        </a-button>
        <a-button :type="btnStatus === 'undone' ? 'primary' : 'default'"  @click="changeBtnStatus('undone')">
          未完成
        </a-button>
        <a-button :type="btnStatus === 'done' ? 'primary' : 'default'"  @click="changeBtnStatus('done')">
          已完成
        </a-button>
        </div>
        <a class="clearBtn" @click="clearDone">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
import { mapState, mapGetters} from 'vuex'
export default {
  data() {
    return {
    };
  },
  created(){
    this.$store.dispatch('getList')
  },
  computed:{
    ...mapState(['list','iptValue','btnStatus']),
    ...mapGetters(['unDoneLength','changeItemList'])
  },
  methods:{
    //获取input框的值
    changeIptValue(e){
      this.$store.commit('setIptValue',e.target.value)
    },
    //添加列表项
    addItemTodolist(){
      if(this.iptValue.trim().length===0){
        return this.$message.warning('文本框的内容不能为空!')
      }
      this.$store.commit('addItemTodolist')
    },
    //删除列表项
    delItemTodolist(id){
      this.$store.commit('delItemTodoList',id)
    },
    //复选框状态更改
    changeckStatus(e,id){
      const params={
        status:e.target.checked,
        id:id
      }
      this.$store.commit('changeCkStatus',params)
    },
    //根据按钮切换数据
    changeBtnStatus(key){
      console.log(key);
      this.$store.commit('changeBtnStatus',key)
    },
    //清除已完成
    clearDone(){
      this.$store.commit('clearDone')
    }
  }
};
</script>

<style scoped>
#app {
  margin: 20px;
}
.my_ipt {
  width: 500px;
  margin-right: 10px;
  margin-bottom: 10px;
}
.my_list{
  width: 500px;
}
.footer{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

//index.js中
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    //初始化的数据
    list: [],
    //获取input框的值
    iptValue: "",
    nextId: 5,
    btnStatus: "all",
  },
  mutations: {
    //初始化数据
    showData(state, list) {
      state.list = list;
    },
    //input框的值
    setIptValue(state, val) {
      state.iptValue = val;
    },
    //添加列表项
    addItemTodolist(state) {
      const obj = {
        id: state.nextId,
        info: state.iptValue,
        done: false,
      };
      state.list.push(obj);
      state.nextId++;
      state.iptValue = "";
    },
    //删除列表项
    delItemTodoList(state, id) {
      const i = state.list.findIndex((v) => v.id === id);
      state.list.splice(i, 1);
    },
    //复选框状态更改
    changeCkStatus(state, params) {
      const index = state.list.findIndex((v) => v.id === params.id);
      if (index !== -1) {
        state.list[index].done = params.status;
      }
    },
    //根据按钮切换数据
    changeBtnStatus(state, key) {
      state.btnStatus = key;
    },
    //清除已完成
    clearDone(state){
      state.list = state.list.filter(v => v.done!==true)
    }
  },
  actions: {
    getList(context) {
      axios.get("/index.json").then((res) => {
        context.commit("showData", res.data);
      });
    },
  },
  getters: {
    unDoneLength(state) {
      return state.list.filter((v) => v.done !== true).length;
    },
    changeItemList(state) {
      if (state.btnStatus === "all") {
        return state.list;
      } 
      else if (state.btnStatus === "undone") {
        return state.list.filter((v) => v.done === false);
      } 
      else if (state.btnStatus === "done") {
        return state.list.filter((v) => v.done === true);
      } 
      else {
        return state.list;
      }
    },
  },
  modules: {},
});