我们总以为,是生活欠我们一个“满意”,其实是我们欠生活一个“努力”。
你好,我是梦阳辰!期待与你相遇!
1.Vuex是做什么的?
官方解释:Vuex是一个专为Vue.js 应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex也集成到Vue 的官方调试工具devtools extension,提供了诸如零配置的 time-travel调试、状态快照导入导出等高级调试
功能。
2.状态管理到底是什么?
状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
3.等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?
没错,就是响应式。
如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?
当然也可以,只是自己封装可能稍微麻烦一些。不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。
4.Vuex管理哪些状态呢?
有什么状态时需要我们在多个组件间共享的呢?
如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。比如用户的登录状态、用户名称、头像、地理位置信息等等。
比如商品的收藏、购物车中的物品等等。
这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的。
单页面状态管理,可以参照计数器这个案例。
多个页面使用同一个状态(变量)
Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
多个试图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
不同界面的Actions都想修改同一个状态(Home.vue需要修改,Profile.vue也需要修改这个状态)
也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。
但是状态a/状态b/状态c我们希望交给一个大管家来统─帮助我们管理! !!
没错,Vuex就是为我们提供这个大管家的工具。
全局单例模式(大管家)
我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
之后,你们每个试图,按照我规定好的规定,进行访问和修改等操作。
这就是Vuex背后的基本思想。
安装Vuex(或者在创建项目的时候选择安装):
npm install vuex --save
不要直接修改state,而是按上面的图进行修改state,这样浏览器就可以进行跟踪。
浏览器插件,可以记录每次修改,便与排错。
Action可以进行异步操作。
练习:
App.vue
<template>
<div id="nav">
<h3>App组件内容</h3>
<p>{{$store.state.counter}}</p>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<h3>---HelloWorld组件内容</h3>
<hello-world></hello-world>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name:'App',
components:{
HelloWorld
},
methods:{
addition(){
this.$store.commit('increment')
},
subtraction(){
this.$store.commit('decrement')
}
}
}
</script>
<style>
</style>
HelloWorld.vue
<template>
<div>
<p>{{$store.state.counter}}</p>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
counter:1
},
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--;
}
},
actions: {
},
modules: {
}
})
小结:
1.提取出一个公共的store对象,用于保存在多个组件中共享
的状态
2.将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到
3.在其他组件中使用store对象中保存的状态即可
通过this.$store.state.属性的方式来访问状态
通过this.$store.commit(‘mutation中方法’)来修改状态注意事项:
我们通过提交mutation的方式,而非直接改变
store.state.counter。
这是因为Vuex可以更明确的追踪状态的变化,所以不要直接
改变store.state.count的值。
state单一状态树
Vuex提出使用单—状态树,什么是单—状态树呢?
英文名称是Single Source of Truth,也可以翻译成单—数据源。
但是,它是什么呢?我们来看一个生活中的例子。
OK,我用一个生活中的例子做一个简单的类比。
我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以
及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。
这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。
这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当
然国家目前已经在完善我们的这个系统了)。
这个和我们在应用开发中比较类似︰
如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难.所以Vuex也使用了单—状态树来管理应瓜层级的全部状态。
单一状态树(单一数据源)能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
数据经过变化后:
store/index.js
getters:{
powerCounter(){
return state.counter*state.counter
},
over20stu(state){
return state.students.filter(s=>s.age<20)
},
overAgestu(state){
return function (age) {
return state.students.filter(s=>s.age>age)
}
}
}
App.vue
<p>{{$store.getters.over20stu}}</p>
<p>{{$store.getters.overAgestu(28)}}</p>
Vuex的store状态的更新唯一方式∶提交MutationMutation主要包括两部分︰
字符串的事件类型( type )
一个回调函数(handler ) ,该回调函数的第一个参数就是state。
定义方式:
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--;
}
},
通过mutation更新:
addition(){
this.$store.commit('increment')
}
在通过mutation更新数据的时候,有可能我们希望携带一些额外的参数参数被称为是mutation的载荷(Payload)
但是如果参数不是一个呢?
比如我们有很多参数需要传递.
这个时候,我们通常会以对象的形式传递,也就是payload是一个对象.这个时候可以再从对象中取出相关的信息.
特殊的提交方式:
Mutation中的处理方式是将整个commit的对象作为payload使用,所以代码没有改变,依然如下:
单数payload为对象 即结果为object
Vuex的store中的state是响应式的,当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时,使用下面的方式:
方式一:使用Vue.set(obj, ‘newProp’,123)
方式二:用新对象给旧对象重新赋值
在mutation中,我们定义了很多事件类型(也就是其中的方法名称).
当我们的项目增大时,Vuex管理的状态越来越多,需要更新状态的情况越来越多,那么意味着Mutation中的方法越来越多.
方法过多,使用者需要花费大量的经历去记住这些方法,甚至是多个文件间来回切换,查看方法名称,甚至如果不是复制的时候,可能还会出现写错的情况.
通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
主要的原因是当我们使用devtools时,可以devtools可以帮助我们捕捉mutation的快照但是如果是异步操作,那么devtools将不能很好的追踪这个操作什么时候会被完成.
所以不能在mutation中进行异步操作。
但是某些情况,我们确实希望在Vuex中进行一些异步操作,比如网络请求,必然是异步的.这个时候怎么处理呢?
Action类似于Mutation,但是是用来代替Mutation进行异步操作的.
import { createStore } from 'vuex'
export default createStore({
state: {
counter:1,
students:[
{id:23,name:'mengyangchen',age:19},
{id:24,name:'mengyang',age:20},
{id:24,name:'meng',age:29},
],
info:{name:'梦阳辰',age:20}
},
mutations: {
//方法
increment(state){
state.counter++
},
decrement(state){
state.counter--;
},
updateInfo1(state){
state.info.name='李四'
}
},
actions: {
//context:上下文
updateInfo(context){
setTimeout(()=>{
context.commit('updateInfo1')
},1000)
}
},
modules: {
},
getters:{
powerCounter(){
return state.counter*state.counter
},
over20stu(state){
return state.students.filter(s=>s.age<20)
},
overAgestu(state){
return function (age) {
return state.students.filter(s=>s.age>age)
}
}
}
})
app.vue
<template>
<div id="nav">
<h3>App组件内容</h3>
<p>{{$store.state.counter}}</p>
<p>{{$store.getters.over20stu}}</p>
<p>{{$store.getters.overAgestu(28)}}</p>
<p>{{$store.state.info.name}}</p>
<button @click="addition">+</button>
<button @click="subtraction">-</button>
<button @click="updateInfo2">更新</button>
<h3>---HelloWorld组件内容</h3>
<hello-world></hello-world>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name:'App',
components:{
HelloWorld
},
methods:{
addition(){
this.$store.commit('increment')
},
subtraction(){
this.$store.commit('decrement')
},
updateInfo2() {
this.$store.dispatch('updateInfo')
}
}
}
</script>
<style>
</style>
store/index.js
app.vue
Module是模块的意思,为什么在Vuex中我们要使用模块呢?
Vue使用单—状态树,那么也意味着很多状态都会交给Vuex来管理.
当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state、mutation、action、getters等
我们按照什么样的方式来组织模块呢?
项目结构:
Do one thing at a time, and do well.