文章目录
一、什么是Vuex?
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
1.1 状态管理是什么东西?
状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
-
当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
-
如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。
1.2 管理什么东西呢?
有什么状态是需要我们在多个组件间共享的呢?
如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
比如用户的登录状态、用户名称、头像、地理位置信息等等。
比如商品的收藏、购物车中的物品等等。
1.3 单界面的状态管理
这图片中的三种东西,怎么理解呢?
State:不用多说,就是我们的状态。(姑且可以当做就是data中的属性)
View:视图层,可以针对State的变化,显示不同的信息。
Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变。
1.4 多界面状态管理
二、 Vuex基本使用步骤
2.1 第一步:安装生产环境的vuex
cnpm install vuex --save
2.2 第二步: 在src目录下创建 store文件夹,并创建 index.js
文件
2.3 第三步:配置index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
// 保存状态的
state:{
counter:1000
},
mutations:{
},
actions:{
},
getters:{
},
modules:{
}
})
// 3.导出
export default store
2.4 第四步:在main.js
文件中引入store/index.js
并挂载到Vue实例上
2.5 第五步:使用
这里以我们取的counter
变量为例,怎么在其他组件中使用这个变量呢?如下:
使用方法为 $store.state.counter
原理是 所有在Vue实例中挂载的对象都是,挂载在Vue.prototyoe
上的,可以全局访问。
2.6 第六步:修改
虽然我们可以直接$store.state.counter = XXX
这样来修改,但是 Vue官方不推荐我们这样做,原因是 devtools 跟踪不到我们这样修改的记录。 这里讲下 Vuex
的工作机制
三、Vuex工作机制
由于devtools 跟踪不到我们这样修改的记录。所以我们不能直接更改State
如果我们直接更改,通过下面这种方式的话:
<button @click='$store.state.counter++'>点击</button>
<button @click='$store.state.counter--'>点击</button>
会发现其实vue 的 devtools
不能跟踪到改变
我们可以通过 Vue 的浏览器插件 来查看:
四、mutations同步函数 方法提交修改
我们不能通过直接修改数据的话,那要怎么修改呢?
答案是mutations
,这是更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
注:该方法不支持异步事件,如果有异步操作,必须配合Action一起使用,否则 devtools 会出问题。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
你不能直接调用一个 mutation handler,而是需要以调用 store.commit 方法:
store.commit('increment')
我们在 index.js
文件的mutations
方法中添加如下方法:
import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
// 保存装填的
state: {
counter: 1000
},
mutations: {
// 方法 增加
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
}
},
actions: {
},
getters: {
},
modules: {
}
})
// 3.导出
export default store
然后在App.vue
文件中通过methods 触发 store.commit()
来触发mutations
里我们定义的 Increment 和 decrement
<template>
<div>
<h2>{{message}}</h2>
<h2>{{$store.state.counter}}</h2>
<button @click="add">点击</button>
<button @click="reduce">点击</button>
<hello-vuex></hello-vuex>
</div>
</template>
<script>
import HelloVuex from "./components/HelloVuex";
export default {
name: "App",
components: {
HelloVuex,
},
data() {
return {
message: "我是App.vue",
// counter: 0,
};
},
methods: {
add() {
this.$store.commit("increment");
},
reduce() {
this.$store.commit("decrement");
},
},
};
</script>
<style>
</style>
效果:
可以发现不仅能检测到修改记录,而且还是响应式的。
4.1 Mutation提交风格
官网直达: Mutation
4.1.1 提交载荷(Payload)
你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
4.1.2 对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
store.commit({
type: 'increment',
amount: 10
})
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
五、getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。
就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
使用:
六、state 响应式
state 是支持相应式的,但前提是 已经在store 中初始化好所有所需属性。
如果没有的话,是不行的。
例如:
state: {
info:{
id:'119',
name:'Are',
age:18
}
},
如果我们给 info
对象,新增一个address
属性:
state.info['address'] = “武汉”
那么这个操作是没有响应式的。
正确操作方法应该是使用Vue 的Vue.set()
方法:
Vue.set(state.info,'address','武汉')
此外删除,也是没有响应式的,应该使用Vue.delete()
例:
// state.info['name'] = 'PengSir';//新增没有响应式
// Vue.set(state.info,'name','PengSir')//新增有响应式
// delete state.info.name//删除没有响应式
Vue.delete(state.info,'name')//删除有响应式
七、Action
如果项目中有异步操作的话,需要使用 Action发送给mutations
Action 可以包含任意异步操作。
我们这里使用setTimeout
来模拟以下异步请求
最后修改信息:
看一下结果:
注:Actions
支持mutations
同样的载荷方式和对象方式进行分发:
八、modules 模块管理
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
注:我们写在 modules
里 state
的东西,实际是挂载在 state
上的
8.1 模块里的 state
例如下面这段代码:如果我们在组件中要获取定义在 modulesA
里的 name
属性的话,通过如下代码获取:
$store.state.a.name
const modulesA = {
state: {
name:'The Shy'
},
mutations:{
},
getters:{
},
actions:{
},
}
const modulesB = {
state: {
},
mutations:{
},
getters:{
},
actions:{
},
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a.name // -> The Shy
通过devtools
插件,我们也可以发现它是挂载在 state
上的。
8.2 模块里的 mutations
假如我们有如下 配置,怎么才能调用模块里的 updateName
方法呢?
const modulesA = {
state: {
name:'The Shy'
},
mutations:{
updateName(state,payload){
state.name = payload
}
},
getters:{
},
actions:{
},
}
const store = new Vuex.Store({
modules: {
a:modulesA,
b:modulesB
}
})
方法:
和我们普通方法一样。都是commit即可直接调用到我们模块里的方法,需要注意的是,它是先从外部找 有没有这个方法,没有在找内部。
8.3 模块里的 getters
效果:
8.4 模块里的 actions
九、文件抽离
Vuex的东西 其实还是挺多的 四五个核心函数都放一起的话,看起来未免太乱了,那怎么办呢?
我们可以给它抽离出去:
给它抽离到一个单独的文件,然后使用export default
导出,其他的文件也是一样的方式: