| 🔥Vue全家桶 | 地址 |
| — | — |
| 🔥Vue全家桶之Vue基础指令(一) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120117044 |
| 🔥Vue全家桶之Vue组件化开发(二) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120117322 |
| 🔥Vue全家桶之VueCLI 脚手架V2→V4版本(三) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120117453 |
| 🔥Vue全家桶之webpack详解(四) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120297794 |
| 🔥Vue全家桶之Vue-router路由(五) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120339146 |
| 🔥Vue全家桶之VueX(六) | https://blog.youkuaiyun.com/Augenstern_QXL/article/details/120339600 |
- 配套视频讲解: CoderWhy老师的Vuejs讲解
========================================================================
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
-
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
-
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
状态管理到底是什么?
-
状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
-
其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
-
然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
-
那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
-
当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
-
如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
-
不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。
我们知道,要在单个组件中进行状态管理是一件非常简单的事情。
这图片中的三种东西如下:
-
State:状态
-
View:视图层,可以针对 State 的变化,显示不同的信息
-
Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的变化
<button @click=“counter+=1”>+1
<button @click=“counter-=1”>-1
在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。
counter需要某种方式被记录下来,也就是我们的State。
counter目前的值需要被显示在界面中,也就是我们的View部分。
界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions
这不就是上面的流程图了吗?
ok,先安装 vuex
npm install vuex --save
Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
-
多个视图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
-
不同界面的 Actions 都想修改同一个状态(Home.vue 需要修改,Profile.vue 也需要修改这个状态)
也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个视图,但是也有一些状态(状态a/状态b/状态c)属于多个视图共同想要维护的
-
状态1/状态2/状态3 你放在自己的房间中,你自己管理自己用,没问题。
-
但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!
-
没错,Vuex就是为我们提供这个大管家的工具。
全局单例模式(大管家)
-
我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
-
之后,你们每个视图,按照我规定好的规定,进行访问和修改等操作。
-
这就是Vuex背后的基本思想。
我们创建一个文件夹 src/store,并且在其中创建一个 index.js 文件,代码如下:
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state){
state.count++
},
decrement(state){
state.count–
}
},
actions: {
},
getters: {
},
modules: {
}
})
// 导出store对象
export default store
其次,我们让所有的Vue组件都可以使用这个store对象
- 来到 src/main.js文件,导入store对象,并且挂载到new Vue中
import Vue from ‘vue’
import App from ‘./App’
// 1.导入store对象
import store from ‘./store’
new Vue({
el: ‘#app’,
// 2.挂载 store
store,
render: h => h(App)
})
- 这样,在其他Vue组件中,我们就可以通过
this.$store
的方式,获取到这个store对象了
<button @click=“increment”>+1
<button @click=“decrement”>-1<
使用步骤小结:
-
提取出一个公共的 store 对象,用于保存在多个组件中共享的状态
-
将 store 对象放置在 new Vue 对象中,这样可以保证在所有的组件中都可以使用到
-
在其他组件中使用 store 对象中保存的状态即可
-
通过
this.$store.state
属性的方式来访问状态 -
通过
this.$store.commit('mutation中方法')
来修改状态
-
我们通过提交 mutation 的方式,而非直接改变 store.state.count
-
这是因为 Vuex 可以更明确的追踪状态的变化,所以不要直接改变 store.state.count 的值
-
在 Chrome 网上应用商店 搜索 devtools
-
选择 vue.js devtools ,添加即可
Vuex有几个比较核心的概念:
-
State
-
Getters
-
Mutation
-
Action
-
Module
1.5.1、State 单-状态树
Vuex提出使用单一状态树, 什么是单一状态树呢?
-
英文名称是Single Source of Truth,也可以翻译成单一数据源。
-
我用一个生活中的例子做一个简单的类比,我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。
-
这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当然国家目前已经在完善我们的这个系统了)。
-
这个和我们在应用开发中比较类似:如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难,所以Vuex也使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
1.5.2、Getters基本使用
有时候,我们需要从 store 中获取一些 state 变异后的状态,比如下面的 Store
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
students: [
{id: 100,name: ‘why’,age: 18},
{id: 111,name: ‘kobe’,age: 21},
{id: 112,name: ‘lucy’,age: 25},
{id: 113,name: ‘lilei’,age: 2},
]
},
mutations: {
},
actions: {
},
getters: {
// 获取年龄大于20的学生对象
more20stu(state){
return state.students.filter(s => s.age >20)
}
},
modules: {
}
})
// 导出store对象
export default store
这样我们在其他 .vue 组件中也可以拿到年龄大于20的学生对象
{{$store.getters.more20stu}}
①、Getters作为参数和传递参数
如果我们已经有了一个获取所有年龄大于20岁学生的列表 getters,那么代码可以这样来写
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
students: [
{id: 100,name: ‘why’,age: 18},
{id: 111,name: ‘kobe’,age: 21},
{id: 112,name: ‘lucy’,age: 25},
{id: 113,name: ‘lilei’,age: 2},
]
},
mutations: {
},
actions: {
},
getters: {
// 获取年龄大于20的学生对象
more20stu(state){
return state.students.filter(s => s.age >20)
},
// 获取年龄大于20的学生个数
more20stuLength(state,getters) {
return getters.more20stu.length
}
},
modules: {
}
})
// 导出store对象
export default store
getters 默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
students: [
{id: 100,name: ‘why’,age: 18},
{id: 111,name: ‘kobe’,age: 21},
{id: 112,name: ‘lucy’,age: 25},
{id: 113,name: ‘lilei’,age: 2},
]
},
mutations: {
},
actions: {
},
getters: {
// 获取年龄大于20的学生对象
more20stu(state){
return state.students.filter(s => s.age >20)
},
// 获取年龄大于20的学生个数
more20stuLength(state,getters) {
return getters.more20stu.length
},
// 让用户自己决定获取年龄大于多少
moreAgeStu(state) {
return function(age){
return state.students.filter(s => s.age > age)
}
}
},
modules: {
}
})
// 导出store对象
export default store
这样我们在其他 .vue 组件中就可以传入年龄数值筛选了
{{$store.getters.more20stu}}
{{$store.getters.more20stuLength}}
{{$store.getters.moreAgeStu(8)}}
1.5.4、Mutation状态更新
-
Vuex的store状态的更新唯一方式:提交Mutation
-
Mutation主要包括两部分:
-
字符串的事件类型(type)
-
一个回调函数(handler) 该回调函数的第一个参数就是 state
- mutation 的定义方式
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state){
state.count++
},
decrement(state){
state.count–
}
},
actions: {
},
getters: {
},
modules: {
}
})
// 导出store对象
export default store
- 通过 mutation 更新
<button @click=“increment”>+1
<button @click=“decrement”>-1<
①Mutation传递参数
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数,参数被称为 mutation 的载荷(Payload)
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state){
state.count++
},
decrement(state){
state.count–
},
incrementCount(state,count){
state.counter += count
}
},
actions: {
},
getters: {
},
modules: {
}
})
// 导出store对象
export default store
我们在其他 .vue 组件中来修改状态
<button @click=“increment”>+1
<button @click=“decrement”>-1<
<button @click=“addCount(5)”>+5
<button @click=“addCount(10)”>+5
但是如果参数不是一个呢?
比如我们有很多参数需要传递,这个时候,我们通常会以对象的形式传递,也就是 payload 是一个对象。
这个时候可以再从对象中取出相关的信息。
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
students: [
{id: 100,name: ‘why’,age: 18},
{id: 111,name: ‘kobe’,age: 21},
{id: 112,name: ‘lucy’,age: 25},
{id: 113,name: ‘lilei’,age: 2},
]
},
mutations: {
},
actions: {
},
getters: {
},
modules: {
addStudent(state,stu){
state.students.push(stu)
}
}
})
// 导出store对象
export default store
我们在其他 .vue 组件中来修改状态
<button @click=“increment”>+1
<button @click=“decrement”>-1<
<button @click=“addCount(5)”>+5
<button @click=“addCount(10)”>+5
<button @click=“addStudent”>添加学生
②Mutation提交风格
上面的通过 commit 进行提交是一种普通的方式
Vue 还提供了另外一种风格,它是一个包含 type 属性的对象
import Vuex from ‘vuex’
import Vue from ‘vue’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
students: [
{id: 100,name: ‘why’,age: 18},
{id: 111,name: ‘kobe’,age: 21},
{id: 112,name: ‘lucy’,age: 25},
{id: 113,name: ‘lilei’,age: 2},
]
},
mutations: {
},
actions: {
},
getters: {
},
modules: {
incrementCount(state,payload){
state.counter += payload.count
}
}
})
// 导出store对象
export default store
<button @click=“increment”>+1
<button @click=“decrement”>-1<
<button @click=“addCount(5)”>+5
<button @click=“addCount(10)”>+5
③Mutation响应规则
Vuex 的 store 中的 state 是响应式的,当 state 中的数据发生改变时,Vue组件会自动更新
这就要求我们必须遵守一些Vuex对应的规则:
-
提前在 store 中初始化好所需的属性
-
当给 state 中的对象添加新属性时,使用下面的方式
-
方式一:使用
Vue.set(obj,'newProp',123)
-
方式二:用新对象给就旧对象重新赋值
例如:
我们在 index.js 中增加 info 状态
const store = new Vuex.Store({
state: {
info: {
name: ‘why’, age: 18
}
},
mutations: {
// 方式一:Vue.set()
Vue.set(state.info,‘height’,payload.height)
// 方式二:给 info 赋值一个新的对象
state.info = {…state.info,‘height’:payload.height}
}
})
我们在其他 .vue 组件中修改状态
我的个人信息: {{info}}
<button @click=“updateInfo”>更新信息
④Mutation常量类型-概念
我们来考虑下面的问题:
-
在 mutation 中,我们定义了很多事件类型(也就是其中的方法名称)
-
当我们的项目增大时,Vuex 管理的状态越来越多,需要更新状态的情况越来越多, 那么意味着 Mutation 中的方法越来越多
-
方法过多,使用者需要花费大量的精力的经历去记住这些方法,甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
如何避免上述问题呢?
-
在各种Flux实现中, 一种很常见的方案就是使用常量替代 Mutation 事件的类型
-
我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.
具体怎么做呢?
-
我们可以创建一个文件:
mutation-types.js
, 并且在其中定义我们的常量 -
定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.
我们在src/store 下新建 mutation-types.js
exports const UPDATA_INFO = ‘UPDATE_INFO’
这样的话我们在 index.js 中可以导入
import Vuex from ‘vuex’
import Vue from ‘vue’
import * as types from ‘./mutation-types’
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
state: {
info: {
name: ‘why’,
age: 18
}
},
mutations: {
state.info = {…state.info,‘height’:payload.height}
}
},
actions: {
},
getters: {