17-vuex状态管理
# 一, vuex状态管理
# 1.1, 为什么要使用Vuex
Vue应用程序的状态(state)管理器。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- 把“状态”换成“数据”。
- 如果你的项目中,需要用到在各个子组件中共享数据,则你就需要用到vuex。
# 1.2,示例
设置如下组件:
- 一个vue实例,充当根组件
- 子组件AddNumber
- 子组件SubNumber
希望在两个子组件AddNumber和subNumer之间共同维护和使用数据项:counter
- 在vue实例中:显示 counter的值
- 在子组件AddNumber中:显示 counter的值;添加counter的值
- 在子组件subNumber中:显示 counter的值;减少counter的值
- 由于上面的需要,我们需要在三个地方共同使用同一数据项counter,所以我们需要用到 vuex
第一步:先引入vue.js,再引入vuex.js
<!-- 引入Vue.js -->
<script src="./libs/vue.js"></script>
<!-- 引入Vuex.js -->
<script src="./libs/vuex.js"></script>
2
3
4
第二步:实例化vuex中的store对象
- 实例化一个对象,就是通过new的方式去创建一个对象
// 创建一个仓库 需要传入一个配置对象
let store = new Vuex.Store({
// state是状态 是集中管理的状态
// 状态是响应式的
// 组件中的data中的状态也是状态式
state: {
counter: 0
},
// 修改状态的唯一途径
mutations: {
// 每一个mutations就是一个函数
// 第1个参数是仓库中的state
// payload 是commit mutation时,传递的参数
add(state, payload) {
// state.counter++;
state.counter += payload
},
sub(state) {
state.counter--;
}
},
// 放异步代码,如果有异步操作,代码需要写在actions中
actions: {
},
// 类似于组件中的计算属性
// 根据已有状态计算出一个新的状态
getters: {
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
第三步:注入到Vue实例中
let vm = new Vue({
el: "#app",
store, // vuex也是一个插件,需要挂载到根组件上,就意味着,它的子子孙孙,都可以使用这个仓库了
components: {
AddCounter,
SubCounter
},
data() {
return {
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
第四步:定义两个组件并在App中使用之
let AddCounter = Vue.extend({
template: `
<div>
<p>我是addcounter组件</p>
<p>在addcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
<button @click="add">加1</button>
</div>
`,
methods: {
add() {
// 这样是粗暴地修改状态 极力不推荐
// 如果其它组件也这样修改状态,状态不好追踪了
// this.$store.state.counter++;
// 需要commit一个mutation,通过mutation去修改状态
this.$store.commit("add", 100)
}
}
})
let SubCounter = Vue.extend({
template: `
<div>
<p>我是subcounter组件</p>
<p>在subcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
<button @click="sub">减1</button>
</div>
`,
methods: {
sub() {
// this.$store.state.counter--;
this.$store.commit("sub")
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
第五步:使用store中的数据
- 一旦你在vue的实例中注入了store,则在所有的子组件及 vue的实例中,你都可以通过:this.$store.state. 数据名 去获取数据
- 类似于我们把router注入到vue实例中,我们就可以通过this.$router 和 this.$route操作路由。
<p>在addcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
<p>在subcounter组件中使用仓库中的数据:{{$store.state.counter}}</p>
2
- 可以通过:this.$store.state. 数据名 = 值 去修改数据,但是,vuex反对这么做
你可以有两种方法去使用数据
- 获取:this.$store.state. 数据名
- 修改:在组件内部通过this.$commit方法触发事件,执行mutations当中的对应的方法
# 二, 在vue脚手架中使用vuex
设置如下组件:
- 一个vue实例,充当根组件
- 子组件AddNumber。
- 子组件subNumber。
希望在两个子组件AddNumber和subNumer之间共同维护和使用数据项:counter
- 在vue实例中:显示 counter的值
- 在子组件AddNumber中:显示 counter的值;添加counter的值
- 在子组件subNumber中:显示 counter的值;减少counter的值
# 三, vuex的四个概念
- state: 放数据。在组件之间共享
- mutations: 修改数据 修改数据的唯一途径
- getters: 从state中的数据中,取出一部分来,依据数据项产生新的结果。类似于vue实例中的computed(计算属性)。
- actions: 在对数据实现异步操作时,要用的
# 3.1, store
- 每一个 Vuex 应用的核心就是 store(仓库)
- store是一个容器,包含着vue应用的状态(state)
两大特点
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
- 你不能直接更改 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化
在组件中应该如何去获取保存在vuex. 中的数据
- 方法一:this.$store.state.xxx
- 方法二:变通一下,在组件内部用一个计算属性来获取数据
- 方法三:mapState
mapState
- this.$store.state.count写起来比较麻烦。在vuex中提供一个便捷的写法:mapState。
- 它的作用:是把写在vuex.store.state中的数据直接映射到组件中计算属性中。
# 3.2, getters
有时希望在state中的数据基础上,派生出一些其它的数据,此时可以使用getters
在组件中使用getters
- 方法一:this.$store.getters.failedNumber
- 方法二 :mapGetters
- 与mapState类似,它的作用也是用来帮助我们去简化代码。我们如果直接定义一个计算属性也是可以的。
# 3.3, mutations
作用是:它是唯一的用来修改数据的工具
在组件中使用的方式
- 方法一:this.$store.commit
- 方法二:mapMutations
# 3.4, actions
在mutations中,如果操作是异步的,在控制台中并不能追踪到
- 可以把异步操作写在actions中
- 虽然把异步操作写在actions中,但是改变状态的唯一方式是不变,还是通过mutations
action与mutations的区别
- actions 提交的是 mutations,而不是直接变更状态。
- actions 可以包含任意异步操作。
- action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,可以通过context.commit提交,也可以通过context.state获取state。
在组件中使用actions
- 方法一: this.$store.dispatch(“actions名”)
- 方法二:mapActions
# 四, Vuex版本的TodoMVC
目标
# 4.1, 分析
状态:
- 准备两个数据项。放在vuex中,供组件去使用
- todos:[{text:"学习vuex, So easy~", done:false}, {text:"学习React, So easy~", done:false}]
- visibilty:,筛选的条件 :all completed active
功能:
- 显示todos数据
- 添加一条todos数据
- 删除todos数据
- 修改某一项的状态:完成,没有完成之间切换
- 批量修改状态: 全部完成,全部未完成
- 统计没有完成的数量
- 批量删除已经完成的
- 三种状态的筛选
- 编辑todos的内容
- 本地存储
组件:
- 在脚手架工具的基础上,额外设置两个组件
# 4.2, 准备静态资源
第一步:准备静态资源,如下:
第二步:把App.vue中的template的内容用todo.html的内容来代替
第三步:在main.js中引入对应的css
# 4.3, 创建并引入仓库
在项目中安装vuex,如下:
- npm i vuex@3.6.2
创建仓库,如下:
在main.js中去引入
# 4.4, 拆分组件(显示todo数据)
创建ListTodo,如下:
在app.vue组件中,使用Listtodo组件,如下:
把仓库中的todo数据,映射到app.vue组件中,如下:
有多个todo, 就循环出多少个ListTodo组件,如下:
通App.vue这个父组件给ListTodo这个子组件进行数据传递,如下:
在子组件中,设置props接收之,如下:
修改仓库中的状态,如下:
效果如下:
# 4.5, 添加一条todos数据(AddTodo组件)
抽离AddTodo组件,如下:
在app.vue中使用,如下:
效果如下:
下面就要具体去实现添加一条todo的功能
- 本质上需去vuex.store.state.todos中加一条记录,这里就是涉及修改vuex中的数据,必须要用mutations
- 所以,我们应该先去vuex中去创建好一个mutations,如下:
把这个方法直接映射到addTodo组件的methods中,如下:
# 4.6, 删除一条todos数据
需求:
删除操作也是要去修改数据,则对应的也要建立一个mutations。如下:
这个删除操作是在listTodo组件中完成的。在 listTodo组件中给删除的按钮绑定事件,如下:
# 4.7, 修改某一项的状态:完成,没有完成之间切换
说白了,就是修改vuex中每个todo的状态(done),如下:
这个操作,本质就是要去修改某一个todo项的done属性。所以也需要在mutations中去写好对应的方法。如下:
在ListTodo,映射成方法,这个工作是在ListTodo组件完成的,我们去给按钮加change事件。如下:
浏览器测试之,如下:
# 4.8, 批量修改状态: 全部完成,全部未完成
首先,我们要去判断当前情况下,是否所有的任务已经全部完成。如果全部完成,则它应该要处于check状态。现在,我们需要在原始数据的基础,加工得到一个新数据:是否全部完成。所以,应该要在vuex.store中的加一个getters,如下:
在app.vue中使用之,如下:
浏览器测试之,如下:
现在只是实现了:根据是否全部完成来设置input的checked。下面,我们还要给它加change事件:
- 如果当前是处于checked ,则点击事件发生,要全部改成done:false
- 如果当前是没有选中,则点击事件发生,要全部改成done:true
上面的操作,也是要修改vuex中仓库的数据,则对应地要去建立mutations,如下:
在app.vue使用之,如下:
使用toggleAllTodo,如下:
浏览器测试之,如下:
# 4.9, 统计没有完成的数量
统计没有完成的数量,说白了,就是数一数,todos中todo的done是false个数,不需要对数据进行修改,只是在原始数据的基础上,加工得到新数据。去vuex.store中设置一个 Getters,如下:
把它map到app.vue组件中,成为一个计算属性。如下:
使用之,如下:
测试之,如下:
# 4.10, 批量删除已经完成的
这个按钮,只有在“完成了数量>0”的情况下才可见。
- todos.length是所有任务的数量
- unDoneNumber: 还没有完成的数量
- 当Todos.length >unDoneNumber 说明,有一部分是已经完成了的。此时应该要显示这个按钮
下面,接给它添加点击事件,点击之后,要把已经完成的任务从整个的todos当中删除掉。由于这里涉及修改数据 ,我们需要去建立 一个mutations。如下:
下面,把这个功能映射到app.vue组件的methods 中。如下:
给按钮加点击事件,调用这个方法,如下:
浏览器测试之,如下:
# 4.11, 三种状态的筛选
这个操作不会更改数据,只是在原数据的基础上进行筛选,得到新数据。所以,我们去创建getters,如下:
每次点击不同的按钮,就相当于是要去修改visibility,所以,我们要去设置一个mutations,如下:
把上面的changeVisibility映射到app.vue组件的方法中去。如下:
使用之,如下:
浏览器测试之,如下:
最重要的一点:现在的数据的来源不是整个的todos,而是根据当前条件过滤的数据,如下:
使用之,如下:
浏览器测试之,如下:
点击样式进行切换,如下:
浏览器测试之,如下:
# 4.12, 编辑todos的内容
当我们在todo的内容上双击时,则会处于编辑状态:
开启编辑状态:加一个editing这个类.
- 在listtodo中,设置双击加一个类的效果
- 给listTodo这个组件加一个isEdit数据项,用它来表示当前是否处于编辑状态
并不是说所有的状态都放在vuex中比较好,如果一个状态只有在一个组件中使用,好么不需要放到vuex中,我们直接把isEdit状态放到组件中,如下:
对文件框和整个的li,它们在编辑状态会有不同的样式:
在浏览器中测试之,如下:
当双击todo时,需要启动编辑,如下:
对输入框来说,初始值就是当前的todo的text,如下:
测试之,如下:
当失去焦点,或者是回车时,都应该结束编辑
但,结束编辑时,应该去修改数据。我们应该定义 一个mutations去处理这种情况。如下:
下面,我们把这个mutations映射到listTodo组件的methods中去。如下:
修改输入框的代码,如下:
修改finishEdit方法,如下:
浏览器测试之,如下:
# 4.13, 本地存储
新建一个模块:
当数据变发生变动,就需要存储数据,在vuex中改变数据的唯一途径就是mutation,所以,只要触发mutaion,说明就要修改数据了,所以说,在每 个mutation中,就需要保存数据了,在store/index.js中引入这个模块,如下:
浏览器测试之,如下:
需要在localStorage中取数据,如下:
浏览器测试之,如下: