前言
之前的文章介绍了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,
done: false,
};
//将对象存到数组中
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: {},
});