Vuex是用来对vue项目中,多个组件的共享状态进行集中的管理(读和写)。通过action行为触发定义好的修改方式来修改全局变量,以及getters获取全局变量,能够让我们方便地管理这些全局变量。
本文将介绍:
一、什么是Vuex
在学习如何使用Vuex之前,我们要先学习到 Vue 中单项数据流的概念。
1.1 单项数据流
一个单项数据流中包含三个元素:
State: 驱动应用的数据源;
View: 以声明方式将 State 映射到视图
Actions: 响应在 view 上的用户输入导致的状态变化
在使用时,通过 Actions 修改变量的状态,变量状态修改后,变量通过映射的方式引起视图变化
在Vue文件中,上述三种元素提现为:
<template>
<div class="Item">
{{myData}} // view: 通过双夸号声明的方式,将myData数据显示到视图层上
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
myData: 'this is my test' // state:数据源,用来承载数据
}
},
methods: {
changeMyData: function() { // action: 动作行为。用来修改数据状态(内容)
this.myData = 'myData is changed'
}
}
}
</script>
1.2 为什么会出现Vuex
由于Vue中单项数据流的变量管理模式,当出现多个组件同时依赖同一个数据,同时会修改这个数据,并响应到页面上的情况时,单向数据流模型就会遭到破坏,变得复杂。最直接的就是数据难以传输和管理。而 Vue 提供的组件间传值方式,在多个共享变量,多组件共享的情况下,无疑太多复杂。
如下图:
当出现多个组件共享数据AData,并都会去修改这个共享数据AData的状态时,若只是在子组件(如组件B、组件C)中做修改,无法做到在子组件中修改了数据内容,自动重新渲染其他组件的内容。
1.3 Vuex的实现思路
基于上面的共享变量需要被多个组件同时使用和修改状态,Vue将这些共享变量都提取出来,统一存储,并提供一个Action修改方法,提供一个Getter方法获取这个共享变量。这样就形成了一个全局单例模式管理,在这种模式下,所有的组件树都能够自由获取状态或者触发行为,但这种行为只能根据设定好的规则触发和获取,有利于未来代码的维护和变更。
从Vue官网提供的这张图就可以看出Vuex的实现模式,即将Vuex作为一个全局的单例单向流结构,提供给所有组件使用。
二、Vuex的使用
2.1 文件基本模式
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userName: 'xiaowang'
},
getters: {
getName () {
return state.userName
}
},
mutations: {
setUName(state:any, name:string) {
state.userName = name
}
},
actions: {
setUserName({ commit, state }, name:number) {
commit('setUName', name)
}
},
modules: {
}
})
2.2 核心模块
2.2.1 State 单一状态树
每一个Vue项目都只有一个单一状态树,其作为唯一数据源存在,让我们能直接定位到特定的数据片段。
state: {
loadingProcess: 0,
popPage: 'loading'
},
2.2.2 Mutation 状态修改事件
当要修改 Vuex 中存储的状态,唯一方法就是是提交 mutation,通过传值的方式修改 State 中存储的特定变量。
mutation事件中提供state参数和payload载荷,state参数可以看作是单一状态树具体化的一个对象,可以获取到单一状态树内的数据,payload载荷其实是一个json形式对象,也可以直接传变量。
// 定义
export default new Vuex.Store({
state: {
userName: 'xiaowang',
age: 18
},
mutations: {
serUser(state:any, payload:any) {
state.userName = payload.name
state.age = payload.age
},
setUName(state:any, name:string) {
state.userName = name
}
}
})
// 使用载荷
store.commit('serUser', {
userName: 'lisi',
age: 22
})
// 直接传值
store.commit('setUName', userName: 'lisi')
但由于Mutation只能是同步,如果在Mutation中混合了异步操作,如http请求,则会导致线程卡住,且难以调试。Mutation应该更注重于State的变化,而不是修改State的值的来源逻辑。这应该放在Action中使用
2.2.3 Action 修改动作
Action和 Mutation很像,都是通过外部调用来修改state,但实际Action也是通过调用Mutation来间接修改State值的。
他们的具体区别在于:
- Action是通过提交Mutation来修改状态的,而Mutation才是直接改变状态
- Action中可以包含任意异步操作,可以在Action中使用http等任意异步操作,并在回调函数中通过 commit 触发Mutation来修改状态值。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userName: 'xiaowang'
},
getters: {
getName () {
return state.userName
}
},
mutations: {
setUName(state:any, name:string) {
state.userName = name
}
},
actions: {
setUserName({ commit, state }, name:number) {
axios.post(Api.getName).then{
(res) => {
commit('setUName', res) // 获取到异步请求的数据后,提交mutations修改state值
}
}
}
},
modules: {
}
})
三、实战:移动端上,使用Vuex控制统一弹窗界面的显示
在移动端中,弹窗界面一般只会显示一个,如果用传统的方式来控制弹窗之间的切换时,需要用js代码主动遍历一遍所有的弹窗,确定他们都被关闭了。或是在按键控制时,频繁的操控弹窗的打开和关闭。
Vue中提供了v-if来控制界面是否显示,我们可以将Vuex和v-if结合起来,在Vuex中存储一个唯一的弹窗变量,来表明当前是哪一个弹窗。同时由于Vuex的实时渲染,其他打开的弹窗就会自动隐藏起来。以下是项目代码:
store.js ///
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
popPage: 'loading'
},
getters: {
// 获取当前弹窗
getPopPageName: (state) => {
return state.popPage
}
},
mutations: {
setPopPages (state:any, pageName:string) {
state.popPage = pageName
}
},
actions: {
switchPopPages ({ commit, state }, pageName:string) {
commit('setPopPages', pageName)
}
},
modules: {
}
})
<!-- tom 首页 -->
<template>
<div class='Home'>
<div class="AgentCenter" @click="jumpToPage('/agentCenter')">
<div class="AgentImg"></div>
</div>
<!-- tom 用户中心-->
<ComDialog ref="userCenter" v-if="state.getters.getPopPageName === 'userCenter'">
<div slot="dialog-title">用户中心</div>
<div slot="dialog-content">用户名称</div>
</ComDialog>
<!-- tom 设置-->
<ComDialog ref="setting" v-if="state.getters.getPopPageName === 'setting'">
<div slot="dialog-title">设置中心</div>
<div slot="dialog-content">音量</div>
</ComDialog>
<!-- tom 消息-->
<ComDialog ref="message" v-if="state.getters.getPopPageName === 'message'">
<div slot="dialog-title">消息中心</div>
<div slot="dialog-content">消息</div>
</ComDialog>
</div>
</template>
<script>
export default {
name: 'Home',
data () {
return {
}
},
methods: {
// 可以调用这个方法,来打开某个弹窗,其他弹窗会自动隐藏
openPop: function (url) {
this.$state.switchPopPages(url)
}
},
created () {
},
mounted () {
}
}
</script>