Vuex图解
组件调用 actions, fetchTodos 加载数据:多个操作, 进度条开始转圈,发送网络请求, 获得数据,加载完成,进度条定制转圈, 显示数据
action中调用的第一个 mutation : 修改加载状态, notLoading ---> Loading
axios HTTP客户端,网络库, Http GET 请求
请求成功后:
index.html
<!doctype html>
<html lang="en">
<head>
<title>Vuex 备忘录</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<div id="app"></div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
List.vue
<template>
<ul class="list-group mt-5">
<!-- todos 不是 props, 不是 data, 是 vuex Store 中 state -->
<!-- 通过 mapState 获得 -->
<li
v-for="(item, index) in todos" :key="index"
class="list-group-item">{{item}}
<!-- 方案一 -->
<!-- <button
@click="rm(index)"
type="button"
class="btn btn-outline-danger btn-sm float-right">X</button> -->
<!-- 方案二 -->
<!-- 通过 mapActions, 在事件处理中直接回调 actions 中的方法 -->
<button
@click="removeTask(index)"
type="button"
class="btn btn-outline-danger btn-sm float-right">X</button>
</li>
</ul>
</template>
<script>
import { mapActions, mapState } from "vuex";
export default {
name: 'List',
// 获得状态的 方案一
// computed: {
// list: function() {
// // this 当前 Vue 实例
// return this.$store.state.todos;
// }
// },
// 获得状态的 方案二
computed: mapState(
[
'todos'
]
),
// 调用 action 方案一
// methods: {
// rm: function(i) {
// // action
// // 繁琐
// this.$store.dispatch('removeTask', i);
// },
// }
// 方案二
// mapActions: 映射到 actions
// 在当前组件中,直接调用 vuex 中定义的 action
// 不再需要定义 rm 方法间接的调用 $store.dispatch()
methods: mapActions(
[
'removeTask'
]
)
}
</script>
Todo.vue
<template>
<div class="mt-5">
<!-- 输入框 -->
<div class="form-group">
<input
v-model="task"
type="text"
class="form-control form-control-sm"
:placeholder="$store.state.input_hint">
</div>
<!-- 按钮 -->
<button
@click="newTask"
type="button"
class="btn btn-primary btn-lg btn-block">创建</button>
</div>
</template>
<script>
export default {
name: 'Todo',
data() {
return {
task: ''
}
},
methods: {
newTask: function() {
// vue 原始 事件冒泡
// this.$emit('event', this.task);
// vuex
// 可以,但是不推荐:子组件中直接操作状态
// this.$store.state.todos.unshift(this.task);
// vuex 推荐 mutations
// 1) 回调
// 2)负载的数据(对象)
// this.$store.commit('createTask', this.task);
// 最佳实践
// event --> action --> mutation --> state --> View(组件)
// 分发(dispatch)给 action
this.$store.dispatch('createTask', this.task);
// 负载
// this.$store.commit(
// 'createTask',
// {
// a: 1,
// b: 2,
// info: this.task
// }
// );
// 组件中的方法修改状态 store.state
// 提交到 变化/突变
// this.$store.commit('mutation'); // 1 简单的同步场景
// 分发到 动作/行为
// this.$store.dispatch('action'); // 2 复杂的所有场景,包含了 1
}
}
}
</script>
index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 引入常量
import { CREATE_TASK, REMOVE_TASK, OTHER } from "./mutation_name";
// Vue 启用了 Vuex 插件,进行集中式的状态管理
Vue.use(Vuex)
export default new Vuex.Store({
// 状态(集中所有组件的状态数据)
// 状态是 响应式(双向绑定的)
state: {
title: '备忘录 | Vuex',
input_hint: '今日事今日毕',
todos: ['起床', '吃饭', '打豆豆']
},
// 同步执行的操作,提供给 action 调用 (commit)
mutations: {
REMOVE_TASK(state, n) {
state.todos.splice(n, 1);
},
CREATE_TASK(state, data) {
state.todos.push(data);
},
OTHER(state) {
console.log('mutation other ' + state.title);
}
},
// 可以包含多个 mutations 的(异步)操作,推荐调用 actions
actions: {
// action 第一个参数为 context (上下文)
// context.commit() 调用 mutation
createTask(context, data) {
// action 提交给 mutation (名字是常量)
context.commit(CREATE_TASK, data);
context.commit(OTHER);
},
// context 调用 mutation
removeTask(context, id) {
context.commit(REMOVE_TASK, id);
}
},
// 模块化状态管理(拆分成多个文件分别维护和管理)
modules: {
},
// 读取状态信息
getters: {
// 函数定义
appTitle: function(state) {
return state.title;
},
// 箭头函数,一个参数时,括号可选
titleInfo: (state) => {
return state.title;
}
}
})
mutation_name.js
// mutations 的名字(一种建议,不是强制的
export let CREATE_TASK = 'CREATE_TASK';
export let REMOVE_TASK = 'REMOVE_TASK';
export let OTHER = 'OTHER';
App.vue
<template>
<div class="container">
<div class="jumbotron">
<!-- 方案一:间接、直接 -->
<!-- 直接访问状态:$store.state -->
<!-- <h1 class="display-4">{{ $store.state.title }}</h1> -->
<!-- 方案二:多定义的一个函数,【画蛇添足】-->
<!-- 通过 getters 方式:$store.getters -->
<!-- <h1 class="display-4">{{ $store.getters.titleInfo }}</h1> -->
<!-- 方案三:在方案二的基础上 mapGetters -->
<!-- <h1 class="display-4">方案三</h1> -->
<h1 class="display-4">{{ titleInfo }}</h1>
<!-- <h1 class="display-4">{{ appTitle }}</h1> -->
</div>
<Todo />
<List />
</div>
</template>
<script>
// import HelloWorld from './components/HelloWorld.vue'
import Todo from "./components/Todo";
import List from "./components/List";
// 导入 getters 映射,启用方案三
import { mapGetters } from "vuex";
export default {
name: 'App',
components: {
Todo,
List
},
// 方案三
computed: mapGetters(
[
'appTitle',
'titleInfo'
]
)
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
// 导入了 store
import store from './store'
Vue.config.productionTip = false
// 程序中的 Vue 实例
new Vue({
// this.$store
store,
render: h => h(App)
}).$mount('#app')
效果如图所示: