Vuex
1. 介绍
什么是Vuex
:
官网:https://vuex.vuejs.org/zh/
Vuex 是一个专为 Vue.js 应用程序开发的数据状态管理模式。
它采用集中式存储管理应用中所有组件的共享数据,Vuex是组件之间数据共享的一种机制。
2. 为什么要有Vuex
父子组件传值或兄弟组件传值,太麻烦了,不好管理。
vuex提供了一种全新的数据共享管理模式,该模式相比较简单的组件传值更加高端、大气、上档次。
注意
:
- 只有组件间共享的数据,才有必要存储到vuex中,组件自己私有的数据,还是要存储到自己的data中;
- 在应用的时候要因地制宜,普通组件传值 和 vuex 适合应用在不同场合,要注意区分。
3. 初始化vuex
步骤
:
-
安装vuex
npm i vuex
-
main.js做如下设置
import Vue from 'vue' import App from './App.vue' // 使用Vuex // 1. 导入vuex模块 import Vuex from 'vuex' // 2. Vue对Vuex进行注册 // 之后在组件内部就可以使用vuex的相关成员了 // Vue.prototype.$store = xxx Vue.use(Vuex) // 插件应用 // 3. 实例化vuex对象,并做具体配置 // new Vuex.Store() 是固定用法 // store:名字暂时是固定的,因为后续对其有使用 const store = new Vuex.Store({ // state:固定标志,给Vuex配置"共享数据",通过对象成员体现 state: { // 具体数据,数据名称可以自定义,数量不限制 count: 25, count1: 26 // …… } }) Vue.config.productionTip = false new Vue({ // 4. 对vuex进行挂载 store, // 简易成员赋值,store:store render: h => h(App) }).$mount('#app')
4. 访问vuex共享的数据
4.1. state
语法
:
this.$store.state.xxx // 组件内,xxx:代表共享数据的名称
$store.state.xxx // 模板中,xxx:代表共享数据的名称
应用
:
给First.vue设置如下代码:
<template>
<div id="first">
<p>我是大哥组件</p>
<p>count的值为:{{$store.state.count}}</p>
</div>
</template>
注意
:
- vuex数据既可以在模板中被访问,语法 $store.state.xxx。
- 也可以在组件实例内部被访问,语法 this.$store.state.xxx。
4.2. mapState
组件中的Vuex数据如果需要频繁被访问,那么类似这样的代码 this.$store.state.xxx 或 $store.state.xxx 需要被重复编写,显然,这个代码有点过于复杂,会增加很多工作量,有没有简便的方式处理呢?
可以使用mapState方式访问state成员
使用步骤
:
-
在应用组件里边按需导入 mapState 辅助函数:
import { mapState } from "vuex";
-
定义计算属性:
computed: { // 通过展开运算符,把state中的数据映射为计算属性 ...mapState([xx,yy,zz……]) }
// mapState函数本质为如下: function mapState(arr){ return { arr[0]:function(){ return this.$store.state.arr[0] }, arr[1]:function(){ return this.$store.state.arr[1] } …… } }
-
然后在组件中就可以像访问data一样访问Vuex成员了。
使用示例
:
在Second.vue组件中通过mapState方式设置并访问共享数据:
<template>
<div id="second">
<p>我是小弟组件</p>
<p>count的值为:
<!--类似访问普通data成员一样,访问vuex数据-->
{{count}}
{{count1}}
</p>
</div>
</template>
<script>
// 通过mapState对共享数据做简化获取处理
import { mapState } from 'vuex'
export default {
name: 'second',
// 计算属性
computed: {
// 处理mapState,三个点是做展开运算的,运算结果与后边的内容是一致的
...mapState(['count', 'count1'])
// 通过mapState对下述成员做简化处理
// ...mapState(['count']) 是做展开操作,获得类似如下的函数
// count: function () {
// return this.$store.state.count
// },
// count1: function () {
// return this.$store.state.count1
// }
}
}
</script>
注意
:
- 如果有多个成员都要经过mapState配置,可以这样
...mapState([xx,yy,zz])
。 - mapState是把state共享数据配置为computed计算属性的成员。
5. 修改(同步)state共享数据
同步修改store中state上的值,需要借助mutations。通过mutations对state共享数据进行修改。
5.1. mutations
语法
:
// 在实例化Vuex对象的参数对象中,定义mutations成员(与state平级)对象
mutations:{
// 参数state是固定的,代表vuex本身的state(名称可以自定义,为了可读性就用state即可),
// 可以用以获取其中的共享数据
// 第二个参数可以接收应用层参数信息
方法名称: function(state,形参){
// 通过state修改内部的数据
state.xx = 形参
}
}
调用 mutations
:
// 组件实例调用
this.$store.commit('mutations方法名')
this.$store.commit('mutations方法名',实参)
// 模板中调用
$store.commit('mutations方法名')
$store.commit('mutations方法名',实参)
使用示例
:
-
给vuex声明mutations和相关成员
// mutations:固定标志,对共享数据做同步修改操作 mutations: { // 声明修改数据的各个方法 // mod是自定义名称 // 参数state,可以自定义,但是就用state,可读性好,意思固定,就代表内部state对象成员(但是它们不是同一个对象) // 参数data,是自定义的,可以接收应用层的数据信息 mod: function (state, data) { // 对共享数据最修改 state.count += data } }
-
在First.vue中调用mutations成员
<template> <div id="first"> <p>我是大哥组件</p> <p>count的值为:{{$store.state.count}}</p> <p> <button @click="$store.commit('mod', 10)">修改count</button> </p> </div> </template>
注意
:
- 在组件实例内部可以通过this调用,例如
this.$store.commit('mod',10)
。 - 调用一次或多次都可以。
- 不要对this.$store.state.xxx 直接修改,容易造成数据混乱,因为许多组件都可以这样干。
5.2. mapMutations
组件中的Vuex数据如果需要频繁被修改,那么类似这样的代码 this.$store.commit(xx,yy) 需要被重复编写,显然,这个代码有点过于复杂,会增加很多工作量,有没有简便的方式处理呢?
可以使用mapMutations
使用步骤
:
-
在应用组件中做mapMutations 的模块化导入操作:
import { mapMutations } from "vuex";
-
在methods方法中做如下设置:
methods: { ...mapMutations([xx,yy,zz……]) }
// mapMutations 函数本质为如下: function mapMutations(arr){ return { arr[0]:function(arg){ return this.$store.commit(arg) }, arr[1]:function(arg){ return this.$store.commit(arg) } …… } }
注意
:
-
mapState 是在computed中做展开,mapMutations是在methods中做展开。
-
xx/yy/zz 是mutations成员名称,一个或多个都可以。
-
现在可以像访问methods方法一样直接访问mutations成员。
应用示例
:
在Second.vue中做如下配置:
<template>
<div id="second">
<p>我是小弟组件</p>
<p>
count的值为:
<!--类似访问普通data成员一样,访问vuex数据-->
{{count}}
{{count1}}
</p>
<p>
<!-- 如下代码冗余度很高 -->
<!-- <button @click="$store.commit('mod', 10)">修改count</button>
<!-- 用简便方式实现数据修改mapMutations,让mutations方法转变为methods方法-->
<button @click="mod(10)">修改count</button>
</p>
</div>
</template>
<script>
// 通过mapState对共享数据做简化获取处理
import { mapState, mapMutations } from 'vuex'
export default {
name: 'second',
// 计算属性
computed: {
// 处理mapState,三个点是做展开运算的,运算结果与后边的内容是一致的
...mapState(['count', 'count1'])
// 通过mapState对下述成员做简化处理
// count: function () {
// return this.$store.state.count
// },
// count1: function () {
// return this.$store.state.count1
// }
},
methods: {
// 应用mapMutations
...mapMutations(['mod'])
// 最终要通过 mapMutations 获得如下相关的方法
// ...mapMutations(['mod']) 做展开操作,获得类似如下的函数
// mod (arg) {
// return this.$store.commit('mod', arg)
// }
}
}
</script>
6. 修改(异步)state共享数据
mutations 和 actions 两个项目都是对Vuex的共享数据做修改的,它们的区别是:
- mutations用在
同步
数据修改操作上 - actions用在
异步
数据修改操作上
什么地方有异步操作:
- ajax/axios
- setTimeout
- fs.readFile()
- ……
6.1. actions
通过actions实现异步方式修改vuex的数据信息
声明语法
:
// actions:固定标志,对共享数据做异步修改
actions: {
// 声明修改数据的成员方法,数量不限制
// 参数context:可以自定义,但是就用context(官方就用之),是一个对象,
// 具体代表$store(它们不是同一个对象)
// 参数data:可以自定义名称,用于可以接收应用层的数据信息
成员名称: function (context, data) {
// actions规定要通过调用mutations成员修改数据(vuex内部规定的)
context.commit(mutations成员,实参)
}
}
调用语法
:
this.$store.dispatch('xx',参数) // 组件内部,xx:代表actions内部成员名称
$store.dispatch('xx',参数) // 模板,xx:代表actions内部成员名称
使用示例
:
-
在vuex中通过actions声明成员,实现异步修改数据操作
actions: { xiu: function (context, data) { // 通过setTimeout实现“异步”请求,定时1s后执行 setTimeout(() => { context.commit('mod', data) }, 1000) } } // 请求过来后停顿1秒后再操作,体现异步
-
First.vue中调用actions方法
<p> <button @click="$store.dispatch('xiu',10)">异步修改count</button> </p>
注意
:
- actions成员内部既可以操作mutations成员,也可以操作state成员。
- 一般不要操作 state,所有数据修改都通过mutations进行,尽管这样有点繁琐,但是好处是数据管理比较集中、专业,体现了只有mutations才拥有修改数据的特权。
6.2. mapActions
组件中的Vuex数据需要频繁被修改,那么类似这样的代码 this.$store.dispatch(xxx) 需要被重复编写,显然,这个代码有点过于复杂,会增加很多工作量,有没有简便的方式处理呢?
可以使用mapActions。
使用步骤
:
-
在应用组件中做mapActions 的模块化导入操作
import { mapActions } from "vuex";
-
在methods方法中做如下设置
methods: { ...mapActions([xx,yy,zz]) }
// mapActions 函数本质为如下: function mapActions(arr){ return { arr[0]:function(arg){ return this.$store.dispatch(arg) }, arr[1]:function(arg){ return this.$store.dispatch(arg) } …… } }
注意
:
- mapActions是在methods中做展开,xx/yy/zz 是actions成员名称,一个或多个都可以。
- 现在可以像访问methods方法一样访问actions成员了。
应用示例
:
Second.vue中设置如下代码:
<template>
<div id="second">
<p>我是小弟组件</p>
<p>
count的值为:
<!--类似访问普通data成员一样,访问vuex数据-->
{{count}}
{{count1}}
</p>
<p>
<!-- 如下代码冗余度很高 -->
<!-- <button @click="$store.commit('mod', 10)">修改count</button>
<!-- 用简便方式实现数据修改mapMutations,让mutations方法转变为methods方法-->
<button @click="mod(10)">修改count</button>
</p>
<p>
<!-- <button @click="$store.dispatch('xiu',10)">修改(异步)count</button> -->
<!-- 异步方式修改数据,用简便方式实现,像调用methods方法一样,去调用actions成员 -->
<button @click="xiu(10)">修改(异步)count</button>
</p>
</div>
</template>
<script>
// 通过mapState对共享数据做简化获取处理
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
name: 'second',
// 计算属性
computed: {
// 处理mapState,三个点是做展开运算的,运算结果与后边的内容是一致的
...mapState(['count', 'count1'])
// 通过mapState对下述成员做简化处理
// count: function () {
// return this.$store.state.count
// },
// count1: function () {
// return this.$store.state.count1
// }
},
methods: {
// 应用mapMutations
...mapMutations(['mod']),
// 最终要通过 mapMutations 获得如下相关的方法
// mod (arg) {
// this.$store.commit('mod', arg)
// }
// actions成员越多,越能体现mapActions的优势
...mapActions(['xiu'])
// 上述语句会生成下述函数
// xiu (arg) {
// 执行动作不需要return
// 要获得返回值才需要return
// this.$store.dispatch('xiu', arg)
// }
}
}
</script>
注意
:
mutations也可以实现异步方式操作数据,为什么不这样做呢?
- devtools调试工具有延迟,造成调试有误差。
- 比较混乱,不成规矩。
- Vuex倡导者也不推荐这样干。
上图来自官网,说明的事情有如下:
- Actions拥有与后端接口直接交互的特权,一般都是异步操作。
- Actions对mutations进行调用,调用 commit。
- mutations的操作可以直观反映在devtools调试工具中,方便程序调试。
- mutations对state进行直接操作。
- state状态数据可以渲染给组件显示。
- 组件实例通过 dispatch 调用actions。