一、Vuex起步
Vuex基本概念
Vuex是在Vue项目开发时使用的状态管理工具,是实现组件全局状态和数据管理的一种机制,可以方便的实现组件之间的数据共享。
为什么使用Vuex
我们在项目开发中频繁的使用组件传参的方式来操作data中的值,项目肯定会变的臃肿,管理和维护这些值将是相当耗时的工作。所以Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具VueX。有了Vuex,我们只需要把这些值定义在VueX中,就可在整个Vue项目的组件中使用。
核心api
- state 存放状态
- mutations state成员操作
- getters 包装state成员给外界
- actions 异步操作
- modules 模块化状态管理
安装
npm i vuex -s
一般呢在项目初始化的时候会有安装选项,勾选安装接着执行下一步即可。这里就先讲下手动安装并配置Vuex相关文件。在src文件夹下新建store文件夹并创建index.js。创建完成的目录如下:
│ App.vue
│ main.js
│
├─assets
│ logo.png
│
├─components
│ HelloWorld.vue
│
├─router
│ index.js
│
└─store
index.js
初始化store
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
getters: {
},
modules:{
}
})
以上初始化Vuex就完成了。
二、TodoList
安装antdv
为了使demo好看些这里使用antdv作为UI库
npm install ant-design-vue --save
引入antdv和挂载:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
//1、导入ant-design-vue组件库
import Antd from 'ant-design-vue'
//2、导入组件库的样式表
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false
//3、挂载组件库
Vue.use(Antd)
new Vue({
store,
render: h => h(App)
}).$mount('#app')
安装axios并mock数据
npm install axios
public文件夹下创建source.json,mock数据
[
{
"id": 0,
"txt": "Racing car sprays burning fuel into crowd.",
"done": false
},
{
"id": 1,
"txt": "Jack car sprays burning fuel into crowd.",
"done": true
},
{
"id": 2,
"txt": "Alien car sprays burning fuel into crowd.",
"done": false
},
{
"id": 3,
"txt": "Wed car sprays burning fuel into crowd.",
"done": true
},
{
"id": 4,
"txt": "Carry car sprays burning fuel into crowd.",
"done": false
}
]
TodoList页面搭建
<template>
<div id="app">
<a-input
class="add_input"
placeholder="请输入添加项"
/>
<a-button type="primary">添加</a-button>
<a-list size="small" bordered :data-source="getSatusList" class="todo_list">
<a-list-item slot="renderItem" slot-scope="item">
<a-checkbox></a-checkbox>
<span>{{ item.txt }}</span>
<a-button type="link">删除</a-button>
</a-list-item>
<div slot="footer" class="footer">
<span>剩余0项未完成</span>
<a-button-group>
<a-button type="primary" @click="setStatus('all')">全部</a-button>
<a-button type="default">未完成</a-button>
<a-button type="default">已完成</a-button>
</a-button-group>
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
export default {
data() {
return {
getSatusList:[] //这里先给列表个空数组,之后删除
};
}
};
</script>
<style>
#app {
width: 460px;
min-height: 300px;
margin: 10px;
padding: 10px;
border: 1px solid #fafafa;
}
.add_input {
width: 361px !important;
margin-right: 10px !important;
}
.todo_list {
width: 435px;
margin-top: 10px !important;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
没数据的情况下就是下面这个样子
axios请求数据
这里使用Action来处理异步任务,如果通过异步操作变更数据,必须通过Action,而不能通过Mutation,但是再Action中还是要通过触发Mutation的方式间接变更数据。
首先state里添加list属性用来存放列表数据,mutations里添加修改state里list的方法,actions里添加请求数据方法,返回数据赋值给state.list
//store index.js
state: {
list: []
},
mutations: {
initList(state, list) {
state.list = list
}
},
actions: {
getList(context) {
axios.get('/source.json').then(({
data
}) => {
context.commit('initList', data);
})
}
}
App.vue文件里引入Vuex,首先created函数中用dispatch触发actions里的getList方法获取数据改变state的list,使用Vuex的mapState方法获取store.list列表,然后绑定到列表组件中。
import { mapState } from "vuex";
export default {
data() {
return {};
},
created() {
this.$store.dispatch("getList"); //触发store异步请求方法
},
computed: {
...mapState(["list"])
}
}
<!--为了省时间,这里省略了上下代码,找到组件对应位置添加list-->
<a-list size="small" bordered :data-source="list" class="todo_list">
渲染效果如下:
双向绑定和列表添加数据
state添加ipatVal属性用来双向绑定输入框
//store index.js
state: {
list: [],
iptVal: ''
},
computed计算属性里获取iptVal
//App.vue
computed: {
...mapState(["iptVal"])
},
绑定输入框并添加change事件
<!--App.vue-->
<a-input
class="add_input"
placeholder="请输入添加项"
:value="iptVal"
@change="iptChange"
/>
store里mutations API中添加修改state.iptVal的方法setIptVal, 然后App.vue页面methods中定义change事件对应的方法从而触发mutations里的方法改变state.iptVal
//store index.js
mutations: {
initList(state, list) { //修改list列表
state.list = list
},
setIptVal(state, val) { //对应列表输入框用来双向绑定
state.iptVal = val
}
}
//App.vue
methods: {
iptChange(e) { //输入框change事件
this.$store.commit("setIptVal", e.target.value);
}
}
到这里就完成了state与视图的双向绑定。接下来是添加列表数据,首先mutations里定义添加方法。
//store index.js
mutations: {
initList(state, list) {
state.list = list
},
setIptVal(state, val) {
state.iptVal = val
},
addItem(state) { //列表添加方法
const obj = {
id: state.list.length,
txt: state.iptVal,
done: false
}
state.list.push(obj)
state.iptVal = ''
}
}
页面加入事件add
<a-button type="primary" @click="add">添加</a-button>
methods定义add并触发mutations里addItem方法。
methods: {
iptChange(e) {
this.$store.commit("setIptVal", e.target.value);
},
add() {
if (this.iptVal.trim().length <= 0) {
return this.$message.warning("输入内容不可为空");
}
this.$store.commit("addItem");
}
}
双向绑定和列表添加就完成了。
删除列表数据
mutations添加删除方法delItem
//store index.js
delItem(state, id) {
const i = state.list.findIndex(item => item.id === id);
if (i > -1) {
state.list.splice(i, 1);
}
}
methods添加del方法
//App.vue
del(id) {
this.$store.commit("delItem", id);
}
接着方法给到button按钮并将id传入
<a-button type="link" @click="del(item.id)">删除</a-button>
至此删除方法也完成了。
勾选选框完成事项
mutations定义改变完成状态方法
//store index.js
checkBoxChange(state, id) {
const i = state.list.findIndex(item => item.id === id);
if (i > -1) {
state.list[i].done = !state.list[i].done;
}
}
methods定义选框change方法
checkChange(id){
this.$store.commit("checkBoxChange", id);
}
选框绑定change方法并传入id
<a-checkbox @change="(e)=>{checkChange(item.id)}" :checked="item.done"></a-checkbox>
完成事项逻辑结束。
清空已完成和显示未完成条数
mutations定义清空完成项的方法
//store index.js
cleanItem(state) {
state.list = state.list.filter(item => !item.done)
}
methods定义清空方法
//App.vue
clean(){
this.$store.commit("cleanItem");
}
清空按钮绑定方法
<a @click="clean">清除已完成</a>
显示未完成事项剩余条数,这里要用到getters API 来将未完成包装一下
//store index.js
getters: {
unDoneLength(state) {
return `剩余${state.list.filter(item=>!item.done).length}项未完成`
}
}
App.vue里引入mapGetters,computed计算属性里获取未完成事项数量方法
computed: {
...mapState(["iptVal"]),
...mapGetters(["unDoneLength"])
}
绑定到视图中
<div slot="footer" class="footer">
<span>{{ unDoneLength }}</span>
切换按钮状态以及根据状态变更列表数据
state添加status状态属性
//store index.js
state: {
list: [],
iptVal: '',
status: 'all' //默认值是所有状态的数据
}
mutations里添加修改status属性的方法
setItemStatus(state, status) {
state.status = status
}
computed计算属性引入status
//App.vue
computed: {
...mapState(["iptVal","status"]),
...mapGetters(["unDoneLength"]),
}
methods中定义触发mutations里status的方法
setStatus(status){
this.$store.commit("setItemStatus", status);
}
给footer里的按钮绑定status来切换按钮状态并绑定用来触发mutations方法的click事件,并传入对应的状态值。
<a-button-group>
<a-button :type="status=='all'?'primary':'default'" @click="setStatus('all')">全部</a-button>
<a-button :type="status=='undone'?'primary':'default'" @click="setStatus('undone')">未完成</a-button>
<a-button :type="status=='done'?'primary':'default'" @click="setStatus('done')">已完成</a-button>
</a-button-group>
最后封装一个getters方法,这个方法会根据不同的状态展示对应状态的数据并反馈到列表。
//store index.js
getSatusList(state) {
switch (state.status) {
case 'all':
return state.list
case 'undone':
return state.list.filter(item => !item.done)
case 'done':
return state.list.filter(item => item.done)
default:
break;
}
}
在computed计算属性中引入
computed: {
...mapState(["iptVal","status"]),
...mapGetters(["unDoneLength","getSatusList"]),
}
由于已经根据不同状态反馈数据到视图中了,所以列表数据源切换到getters封装好的方法getSatusList
<a-list size="small" bordered :data-source="getSatusList" class="todo_list">
至此基于Vuex的TodoList就完成了。