4.基于vuex的案例——todos任务记录工具
4.1我们重新初始化一个vue-cli项目,配置时别忘了选中Vuex配置。
4.2我们还要安装几个依赖:axios和ant-design-vue(ant-designUI组件库,也可以不使用或者使用其他擅长的组件库)
npm install axios ant-design-vue -S
4.3在mian.js中引入ant-design和ant-design的样式文件
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false
Vue.use(Antd)
new Vue({
store,
render: h => h(App)
}).$mount('#app')
4.4实现基本布局
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{item.info}}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div slot="footer" class="footer">
<!-- 未完成的任务个数 -->
<span>0条剩余</span>
<!-- 操作按钮 -->
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
list: [
{
id: 0,
info: 'Racing car sprays burning fuel into crowd.',
done: false
},
{ id: 1, info: 'Japanese princess to wed commoner.', done: false },
{
id: 2,
info: 'Australian walks 100km after outback crash.',
done: false
},
{ id: 3, info: 'Man charged over missing wedding girl.', done: false },
{ id: 4, info: 'Los Angeles battles huge wildfires.', done: false }
]
}
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.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>
4.5在public路径下新增一个list.json文件进行数据存储,并且将我们在App.vue中list里面的数据放到这里。
[
{
"id": 0,
"info": "Racing car sprays burning fuel into crowd.",
"done": false
},
{ "id": 1,
"info": "Japanese princess to wed commoner.",
"done": false
},
{
"id": 2,
"info": "Australian walks 100km after outback crash.",
"done": false
},
{ "id": 3,
"info": "Man charged over missing wedding girl.",
"done": false
},
{ "id": 4,
"info": "Los Angeles battles huge wildfires.",
"done": false
}
]
我们的目的是通过请求这个json文件,来控制我们这个任务列表的内容。
4.6通过axios请求获取到list.json中的数据
在store路径下的index.js文件中引入axios,并且进行异步请求,我们需要将请求到的数据挂载到state中的list数组中,但是由于挂载数据只能在mutation中进行,所以我们要在mutation中声明一个方法,将获取到的数据挂载到list中。在actions中的getList方法获取到数据后,调用mutation中的initList方法,将数据传递给list。
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
list: []
},
mutations: {
initList (state, list) {
state.list = list
}
},
actions: {
getList (context) {
axios.get('/list.json').then(({ data }) => {
context.commit('initList', data)
})
}
},
modules: {
}
})
4.7在App.vue引入state中的list变量,我们通过引入mapState,在computed属性中展开获取。
import { mapState } from 'vuex'
computed: {
...mapState(['list'])
},
4.8输入值的双向绑定
在state中定义一个inputValue属性,默认值为空,存放文本框输入的信息。
并且在App.vue中获取到这个属性后,绑定在标签上。并且给文本框绑定一个change方法,监听input组件值的变化,将这个值传递给state中的inputValue。
<a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handlInputChange"/>
在motation中定义一个接收inputValue的方法:
setInputValue (state, value) {
state.inputValue = value
}
在App.vue中定义handlInputChange方法:
methods: {
handlInputChange (e) {
this.$store.commit('setInputValue', e.target.value)
}
}
4.9 给各个组件绑定事件
4.9.1给添加事项按钮绑定点击事件
在vuex中的state数据中定义nextId属性,用于存放下一项数据的id(添加项的id)
在vuex中定义addItem方法,将id,info(输入内容,trim去除空格),done(是否完成的标识,默认为false未完成)添加到一个对象内;将这个对象push到state属性的list中,并且将nextId数值加一,并且清空inputValue(清空输入框)。
addItem (state) {
const obj = {
id: state.nextId,
info: state.inputValue.trim(),
done: false
}
state.list.push(obj)
state.nextId++
state.inputValue = ''
},
在App.vue中定义一个向列表中新增item项的方法,并且将这个方法绑定给“添加事项”按钮,方法首先判断输入内容是否为空,如果为空则进行提示,不为空则调用vuex中的addItem方法:
addItemToList () {
if (this.inputValue.trim().length <= 0) {
return this.$notification.open({
message: '文本框内容不能为空!',
type: 'warning'
})
}
this.$store.commit('addItem')
},
4.9.2给删除按钮绑定点击事件
在vuex中定义removeItem方法,根据调用该方法时传入的id查找到对应id的索引值,然后使用splice方法将该索引的项删除。
removeItem (state, id) {
// 1.根据id查找对应项索引
const index = state.list.findIndex(i => i.id === id)
// 2.根据索引删除对应项
if (index !== -1) {
state.list.splice(index, 1)
}
},
在App.vue中定义一个向列表中删除item项的方法,并且将这个方法绑定给“删除”按钮,并且将checkbox的id传递给方法,removeItemById方法调用removeIitem方法,将id传入,删除选中项。
removeItemById (id) {
this.$store.commit('removeItem', id)
},
4.9.3给checkbox绑定点击事件
checkbox的点击事件是改变list中选中项的done属性,和删除一样,传入id值后查找索引,如果索引值存在,则改变该项的done属性。
changeDone (state, id) {
const index = state.list.findIndex(i => i.id === id)
if (index !== -1) {
state.list[index].done = !state.list[index].done
}
},
在App.vue中定义changeDoneById方法,并且将这个方法绑定给checkbox,并且将checkbox的id传递给方法,changeDoneById方法调用changeDone方法,将id传入,改变选中项状态。
changeDoneById (id) {
this.$store.commit('changeDone', id)
},
4.9.4给状态选择按钮绑定点击事件
项目底部的状态选择框在点击后需要切换样式,并且获取对应的item项,我们献给这些按钮绑定事件和样式。绑定事件时,将对应的状态传给方法,绑定type时进行判断,如果为当前选中的type,则以primary样式展示,否则以默认样式展示:
<a-button-group>
<a-button @click="ChangeList('all')" :type="viewKey === 'all' ? 'primary' : ''">全部</a-button>
<a-button @click="ChangeList('undone')" :type="viewKey === 'undone' ? 'primary' : ''">未完成</a-button>
<a-button @click="ChangeList('done')" :type="viewKey === 'done' ? 'primary' : ''">已完成</a-button>
</a-button-group>
上面我们用了viewKey判断样式,所以我们需要在vuex的state中定义viewKey,并且定义ChangeList点击事件:
state: {
viewKey: 'undone'
},
changeViewKey (state, key) {
state.viewKey = key
}
},
并且在getters中判断viewKey,如果viewKey为all,返回list;如果viewKey为undone,返回done为false的list;如果viewKey为done,返回done值为true的list:
infoList (state) {
if (state.viewKey === 'all') {
return state.list
} else if (state.viewKey === 'undone') {
return state.list.filter(x => x.done === false)
} else if (state.viewKey === 'done') {
return state.list.filter(x => x.done === true)
} else {
return state.list
}
}
同样,在App.vue中定义ChangeList方法,调用changeViewKey方法,并且返回key值即可
ChangeList (key) {
this.$store.commit('changeViewKey', key)
}
4.9.5给清除已完成按钮绑定点击事件
清除已完成是将done属性为done的项删除,我们在vuex中定义cleanDone方法,将过滤后的list赋值给原来的list即可:
cleanDone (state) {
state.list = state.list.filter(x => x.done === false)
},
在App.vue中定义clean方法调用该cleanDone方法,并且绑定给清除已完成按钮:
clean () {
this.$store.commit('cleanDone')
},
-------------------------------------------------至此,vuex内容完结,附源代码---------------------------------------
github地址:https://github.com/weizhuren/VuexDemo.git
App.vue
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handlInputChange"/>
<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' @click.native="changeDoneById(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 @click="ChangeList('all')" :type="viewKey === 'all' ? 'primary' : ''">全部</a-button>
<a-button @click="ChangeList('undone')" :type="viewKey === 'undone' ? 'primary' : ''">未完成</a-button>
<a-button @click="ChangeList('done')" :type="viewKey === 'done' ? 'primary' : ''">已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a @click="clean">清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
name: 'app',
created () {
this.$store.dispatch('getList')
},
computed: {
...mapState(['list', 'inputValue', 'viewKey']),
...mapGetters(['unDoneLength', 'infoList'])
},
data () {
return {}
},
methods: {
handlInputChange (e) {
this.$store.commit('setInputValue', e.target.value)
},
// 向列表中新增item项
addItemToList () {
if (this.inputValue.trim().length <= 0) {
return this.$notification.open({
message: '文本框内容不能为空!',
type: 'warning'
})
}
this.$store.commit('addItem')
},
// 删除列表中的item
removeItemById (id) {
this.$store.commit('removeItem', id)
},
// 改变任务状态
changeDoneById (id) {
this.$store.commit('changeDone', id)
},
// 清除已完成的任务
clean () {
this.$store.commit('cleanDone')
},
// 修改页面上展示的列表数据
ChangeList (key) {
this.$store.commit('changeViewKey', key)
}
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.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>
vuex(src/store/index.js)
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: 'undone'
},
mutations: {
initList (state, list) {
state.list = list
},
setInputValue (state, value) {
state.inputValue = value
},
// 添加项目列表
addItem (state) {
const obj = {
id: state.nextId,
info: state.inputValue.trim(),
done: false
}
state.list.push(obj)
state.nextId++
state.inputValue = ''
},
// 根据id删除对应任务事项
removeItem (state, id) {
// 1.根据id查找对应项索引
const index = state.list.findIndex(i => i.id === id)
// 2.根据索引删除对应项
if (index !== -1) {
state.list.splice(index, 1)
}
},
changeDone (state, id) {
const index = state.list.findIndex(i => i.id === id)
if (index !== -1) {
state.list[index].done = !state.list[index].done
}
},
// 清除已完成的任务
cleanDone (state) {
state.list = state.list.filter(x => x.done === false)
},
// 修改页面上展示列表数据
changeViewKey (state, key) {
state.viewKey = key
}
},
actions: {
getList (context) {
axios.get('/list.json').then(({ data }) => {
context.commit('initList', data)
})
}
},
getters: {
// 统计未完成的任务条数
unDoneLength (state) {
return state.list.filter(x => x.done === false).length
},
infoList (state) {
if (state.viewKey === 'all') {
return state.list
} else if (state.viewKey === 'undone') {
return state.list.filter(x => x.done === false)
} else if (state.viewKey === 'done') {
return state.list.filter(x => x.done === true)
} else {
return state.list
}
}
}
})
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}