附:「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」
前言
- 源码链接(与本文同步):github.com/hec990/vuex…
使用vuex的好处
1. 统一管理状态的好处
- 能够在 vuex 中集中管理共享的数据,易于开发和后期维护
- 能够高效地实现组件之间的数据共享,提高开发效率
- 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步
2. 什么样的数据适合存储在 vuex 中?
- 一般情况下,只有组件之间共享的数据,才有必要存储到vuex中,对于组件中的私有数据,依旧存储在组件自身的data中即可
核心概念
State
state是唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储
(1)存一个名字为 count 的公共变量
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
// ... 省略其他选项
})
(2)组件中访问该变量
第一种方式
this.$store.state.全局数据名称
示例:
<div>当前最新的count值为:{{this.$store.state.count}}</div>
第二种方式
- 在组件中按需导入 vuex 中的 mapState 函数
import {mapState} from "vuex"
- 将全局数据,映射为当前组件的计算属性(相当于把公共变量引用到我们自己组件中的计算属性,我们可以像使用计算属性一样使用它)
computed:{
...mapState(['count])
}
示例:
<template>
<div>
<div>当前最新的count值为:{{count}}</div>
<button>-1</button>
</div>
</template>
<script lang="js">
import {mapState} from 'vuex' // 1. 导入
export default {
name: "Sub",
computed:{
...mapState(['count']) // 2. 将全局属性映射到computed,做为我们自己的一个计算属性
}
}
</script>
Mutation
Mutation 用于变更 Store 中的数据
- 在vuex中,只能通过 mutation 变更 store 数据,不可以直接操作 store 中的数据【见本mutation例子尾示例】
- 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有的数据变化
(1)mutations中定义函数
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
add(state){
state.count++
}
},
})
(2)第一种方式:组件中使用 mutations 中的 add 函数
- 用commit来触发mutations中的函数
<template>
<div>
<h3>当前最新的count值为:{{this.$store.state.count}}</h3>
<button @click="add1"> +1 </button>
</div>
</template>
<script lang="js">
export default {
name: "addition",
methods:{
add1(){
this.$store.commit('add');
}
}
}
</script>
输出结果:
(3)mutations 可以传递参数
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
add(state){
state.count++
},
// 接收参数
addN(state,step){
state.count += step
}
},
})
(4)组件中使用
<template>
<div>
<h3>当前最新的count值为:{{this.$store.state.count}}</h3>
<button @click="add1"> +1 </button>
</div>
</template>
<script lang="js">
export default {
name: "addition",
methods:{
add1(){
this.$store.commit('add');
},
add2(){
this.$store.commit('addN',10);
}
}
}
</script>
输出结果:
(5)第二种使用方式
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="sub1"> -1 </button>
<button @click="sub2"> -N </button>
</div>
</template>
<script lang="js">
import {mapState,mapMutations} from 'vuex'
export default {
name: "subtraction",
computed:{
...mapState(['count'])
},
methods:{
...mapMutations(['sub','subN']),
sub1(){
this.sub()
},
sub2(){
this.subN(10)
}
}
}
</script>
输出结果:
(5)假设现在要实现一个功能,点击按钮1秒后加1,此时你应该想到直接在mutations函数中加定时器,但是我们要知道定时器是异步的方法,mutation是不允许异步代码的存在,下面来看下 mutations存在异步代码会怎么样?
看到了么,视图变了,但是原本的值没有改变!所以如果有异步方法代码,要注意不能在 mutation 里使用,而是使用 Action
补充:不合法的写法
- 不允许直接修改 state里的数据
- 只能通过 mutation 触发来修改
小结
- 在vuex中只有mutation才有权力修改state里面的数据
- mutation里面只能写同步的代码
Action
- Action用于处理异步任务
- 如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是再 Action中还是要通过触发 Mutation的方式接变更的数据
(1)在 actions 中定义函数
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
add(state){
state.count++
},
addN(state,step){
state.count += step
},
sub(state){
state.count--
},
subN(state,step){
state.count -= step
}
},
actions: {
addAsync(context){
setTimeout(()=>{
context.commit('add')
},1000)
}
},
})
组件中使用 actions 中的函数
- dispatch方法的作用就是触发Action中的某一个函数
<template>
<div>
<h3>当前最新的count值为:{{this.$store.state.count}}</h3>
<button @click="add3">1s后+1</button>
</div>
</template>
<script lang="js">
export default {
name: "addition",
methods:{
add3(){
this.$store.dispatch('addAsync')
},
}
}
</script>
输出结果:1秒后加1
(2)触发 acitons 时携带参数
actions: {
addNAsync(context,step){
setTimeout(()=>{
context.commit('addN',step)
},1000)
},
},
组件中使用
<template>
<div>
<h3>当前最新的count值为:{{this.$store.state.count}}</h3>
<button @click="add4">1s+N</button>
</div>
</template>
<script lang="js">
export default {
name: "addition",
methods:{
add4(){
this.$store.dispatch('addNAsync',20)
}
}
}
</script>
输出结果:
(3)第二种触发 actions 方式
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="sub3">1s后-1</button>
<button @click="sub4">1s后-N</button>
</div>
</template>
<script lang="js">
import {mapActions} from 'vuex'
export default {
name: "subtraction",
methods:{
...mapActions(['subAsync','subNAsync']),
sub3(){
this.subAsync()
},
sub4(){
this.subNAsync(100)
}
}
}
</script>
输出结果:
Getter
- Getter 用于对 Store 中的数据进行加工处理形成新的数据
- Getter不会修改state里的值,只是一个包装作用,相当于计算属性
(1)定义 Getter
getters: {
showNum: state=>{
return '当前最新的数量是['+ state.count +']'
}
}
组件中使用第一种方式
<h3>{{$store.getters.showNum}}</h3>
输出结果:
组件中使用第二种方式
<template>
<div>
<h3>{{showNum}}</h3>
</div>
</template>
<script lang="js">
import {mapGetters} from 'vuex'
export default {
name: "subtraction",
computed:{
...mapGetters(['showNum']),
},
}
</script>
输出结果:
简写方法
- 针对 import 导入的方式,可以直接当计算属性给事件使用,减少了一个函数
<template>
<div>
<h3>{{showNum}}</h3>
<button @click="sub"> -1 </button>
<button @click="subN(10)"> -N </button>
<button @click="subAsync">1s后-1</button>
<button @click="subNAsync(100)">1s后-N</button>
</div>
</template>
<script lang="js">
import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'
export default {
name: "subtraction",
computed:{
...mapState(['count']),
...mapGetters(['showNum']),
},
methods:{
...mapMutations(['sub','subN']),
...mapActions(['subAsync','subNAsync']),
}
}
</script>
输出结果:效果都还是一样的
总结
(1)
- state存储全局变量数据
- mutation相当于方法(同步代码的)
- action处理异步代码
- getter类似于计算属性,对state变量进行一层包装
(2)其他注意事项:
- 在 actions 中,不能直接修改 state 中的数据,必须通过 context.commit() 触发某个 mutation 才行
- 组件中可以直接获取vuex中state的变量,但是只限于展示这个值(官方建议使用 Getters 包装一下再展示),如果要在组件中直接对这个值进行修改会有问题(视图变了,但是值没有变)。state的变量只能是vuex中 mutation 和 action 来进行处理