Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
State
用一个对象就包含了全部的应用层级状态.每个应用将仅仅包含一个.
例如:
state: {
count:0,
list:[],
inputValue:'',
nextId:5,
viewkey:'all'
},
mutations
更改 Vuex 的 store 中的state(状态)的唯一方法是提交 mutation。(tips:不能直接修改state) ,Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数;也可以接收传入的额外的参数(例如:step)。
mutations: {
handleadd(state,step){
state.count += step
},
handlesub(state,step){
state.count -= step
}
}
mutation 处理函数不能直接调用 。“当触发一个类型为handleadd 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,例如:在子组件的 methods 中:(当然你也可以向 store.commit
传入额外的参数 例如 ’3‘)
methods: {
addClick(){
// commit 的作用就是触发调用 mutation 中的函数
this.$store.commit('handleadd', 3)
},
}
注:mutation 必须是同步函数 ,处理异步操作在 actions 中
actions
Action 提交的是 mutation,而不是直接变更状态。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,例如:
// actions 中可以执行异步操作
actions: {
add(context,step) {
setTimeout( () => {
context.commit("handleadd",step)
},2000)
},
sub(context,step) {
setTimeout( () => {
context.commit('handlesub',step)
},2000)
},
}
Action 通过 store.dispatch
方法触发:
methods: {
// 异步
addClickN() {
// dispatch 的作用就是触发调用 action 中的函数
this.$store.dispatch('add',10)
}
}
在 action 内部执行异步操作:
// actions 中可以执行异步操作
actions: {
// 例如获取数据
getList(context) {
axios.get('list.json').then( (data) => {
context.commit('initList',data.data)
})
}
}
modules
每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
getters
有时候我们需要从 store 中的 state 中派生出一些状态,Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性。Store中数据发生变化,Getter的数据也会跟着变化。
例如:
getters:{
showNumber (state) {
return '当前最新的数量是:' + state.count + '。'
},
}
案例:todolist
本地数据源: list.json
[
{
"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
}
]
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:'',
nextId:5,
// 定义一个变量表示底部按钮状态:全部:all ; 未完成:undone ; 已完成:done
viewkey:'all'
},
// mutations 中不能执行异步操作
mutations: {
initList(state,list){
state.list = list
},
setinputval(state,val){
state.inputValue = val
},
additem(state){
const obj ={
id:state.nextId,
info:state.inputValue.trim(),
done:false
}
state.list.push(obj)
state.nextId++
state.inputValue = ''
},
removeitem(state,id){
const i = state.list.findIndex(x => x.id === id)
if(i !== -1){
state.list.splice(i,1)
}
},
// 改变某项任务的 done 的状态
changestatus(state,param){
const i = state.list.findIndex(x => x.id === param.id)
if(i !== -1){
state.list[i].done = param.status
}
},
// 把已经完成的任务过滤掉,返回剩下未完成任务列表
cleandone(state){
state.list = state.list.filter(x => x.done === false)
},
// 改变底部按钮的状态值
changeviewkey(state,key){
state.viewkey = key
}
},
// actions 中可以执行异步操作
actions: {
getList(context) {
axios.get('../list.json').then( (data) => {
console.log(data.data)
context.commit('initList',data.data)
})
}
},
modules: {
},
/*
Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性。
Store中数据发生变化,Getter的数据也会跟着变化。
*/
getters:{
showNumber (state) {
return '当前最新的数量是:' + state.count + '。'
},
// 计算属性统计剩余条数
undone(state){
return state.list.filter(x => x.done === false).length
},
// 列表切换
infolist(state){
if(state.viewkey ==='all'){
return state.list
}
if(state.viewkey === 'undone'){
return state.list.filter(x => !x.done)
}
if(state.viewkey === 'done'){
return state.list.filter(x => x.done)
}
return state.list
}
}
})
example.vue
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt"
:value="inputValue" @change="handleinutchange" />
<a-button type="primary" @click="additemtolist">添加事项</a-button>
<a-list bordered :dataSource="infolist" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<!-- item.done 为 true 时,checkedbox 为选中状态 item.done 为 false 时,checkedbox 为未选中状态 -->
<a-checkbox :checked = "item.done" @change="(e) => {checkchange(e,item.id)}">{{ item.info }}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions" @click="removeitemid(item.id)">删除</a>
</a-list-item>
<!-- footer区域 -->
<div class="footer" slot="footer">
<span>{{undone}}条剩余</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(['inputValue','viewkey']),
...mapGetters(['undone','infolist'])
},
methods: {
// 监听文本框输入变化
handleinutchange(e){
// console.log(e.target.value)
this.$store.commit('setinputval',e.target.value)
},
additemtolist(){
if(this.inputValue.trim().length <= 0){
return this.$message.warning('文本框内容不能为空')
}
this.$store.commit('additem')
},
removeitemid(id){
console.log(id)
this.$store.commit('removeitem',id)
},
checkchange(e,id){
console.log(e.target.checked,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;
}
.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>
需要用到的外部组件 ant-design-vue 这个需要安装。然后导入
import Antd from '../node_modules/ant-design-vue'
import '../node_modules/ant-design-vue/dist/antd.css'