Vuex
1、理解 vuex
1.1、vuex 是什么
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnNwE7vs-1691737144973)(C:\Users\ASUS\Desktop\java\20_vue\image\1687521800157.png)]
1.2、vuex和全局事件总线实现读写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQXFUwHm-1691737144974)(C:\Users\ASUS\Desktop\java\20_vue\image\1687522286162.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wRwOOix-1691737144974)(C:\Users\ASUS\Desktop\java\20_vue\image\1687522407015.png)]
1.3、什么时候使用Vuex(共享)
多个组件依赖于同一个数据
来自不同组件的行为需要变更同一状态
2、案例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JThPYio2-1691737144975)(C:\Users\ASUS\Desktop\java\20_vue\image\1687523629054.png)]
2.1、不使用vuex写
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<select v-model.number="number">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name: "NumberCount",
data() {
return {
sum: 0,
number: 1 //用户选择的数字
}
},
methods: {
increment() {
this.sum += this.number
},
decrement() {
this.sum -= this.number
},
incrementOdd() {
if (this.sum % 2) {
this.sum += this.number
}
},
incrementWait() {
setTimeout(() => {
this.sum += this.number
},1000)
}
}
}
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5b3pUip-1691737144976)(C:\Users\ASUS\Desktop\java\20_vue\image\1687523739878.png)]
2.2、使用vuex
2.2.1、Vuex工作原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83wQfpRx-1691737144977)(C:\Users\ASUS\Desktop\java\20_vue\image\1687526674368.png)]
2.2.2、搭建Vuex环境
-
安装:npm i vuex@3(vue2中要用vuex3,vue3要用vuex4)
-
引入和使用插件:Vue.use(Vuex)
-
创建store,并且再vm创建的时候对store属性赋值
-
vc====>store
-
从main.js中出发
import Vue from "vue";
import App from './App'
//引入vuex
import Vuex from 'vuex'
//使用vuex插件,这段代码必须再 import store之前执行,但是这里没有办法控制,所有写再 store/index.js中请实现
// Vue.use(Vuex)
//引入store
import store from './store/index'
Vue.config.productionTip = false
//只有我们use了Vuex,那么我们就可以再new vm的时候,传入一个store对象了,然后所有的组件就会有store
new Vue({
render: p => p(App),
//再创建vm的时候,赋值store属性
store: store
}).$mount('#app')
- 再在src下创建store/index.js中取创建store
//该文件用于创建Vuex中最为核心的 store,用户管理:Actions、Mutations、State
import Vue from "vue";
//引入vuex
import Vuex from 'vuex'
//使用vuex
Vue.use(Vuex)
//准备actions---用于响应组件中的动作
const actions = {}
//准备Mutations---用于操作数据(state)
const mutations = {}
//准备state----用于存储数据
const state = {}
//创建并且暴露 store
export default new Vuex.Store({
actions,
mutations,
state
})
- 效果:每个组件以及vm都可以看到store,就可以调用对应的API进行实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vD2hJwyk-1691737144978)(C:\Users\ASUS\Desktop\java\20_vue\image\1687531301320.png)]
2.2.3、如何从vuex中读取数据
- 在store/index.js中的state属性中定义 需要被共享的数据
//准备state----用于存储数据
const state = {
sum: 0
}
- 引入我们通过Vue.use(Vuex),所有每个vc和vm都存在 $store,则我们就可以通过它来获取值
<!-- 想办法读取到state中的Sum-->
<h1>当前求和为:{{ $store.state.sum }}</h1>
2.2.4、如何修改vuex中的数据(以加法为例子)
- 按钮点击调用,dispatch方法,并且定义操作的类型和参数
//NumberCount组件中
methods: {
increment() {
//调用dispatch方法
//第一个参数:派遣的行为类型
//第二个参数:加几
this.$store.dispatch('jia', this.number)
}
}
- 在store/index.js中配置actions,并且调用commit方法
- 其实也可以直接在actions中通过:miniStore.state.sum + = value 来操作数据,但是这样的话开发者工具就会失效
const actions = {
//这里需要定义dispatch的操作类型,key:类型;value:回调函数
//简写形式,这个函数可以接收到参数,并且需要调用commit
//接收的第一个参数:简写版本的store上下问
//接收的第二个参数:调用dispatch方法的时候传递的参数
jia(miniStore, value) {
//这里面可以写一些相应的业务逻辑:比如ajax请求等,如果你这里没有定义的业务逻辑,可以不用写actions,但是对应的this.$store.dispatch ,则需要改为 this.$store.commit('JIA',value)
miniStore.commit('JIA', value)
//当jia这个函数体存在很多代码的时候,也可以调用actions中的其它的方法
minStore.dispacth('xxx',value)
}
}
- 在store/index.js中配置mutations,它可以真的的操作到store
//准备Mutations---用于操作数据(state)
const mutations = {
//这里commit调用之后就会执行到mutations中的对应的函数
//参数1:state中的值
//参数2:actions调用commit传递的参数
JIA(state, value) {
state.sum += value
}
}
3、store中的getters配置项
3.1、定义和注册getters
- 定义getters
//配置getters--用于将state中的数据进行加工
const getters = {
//函数会接收到state参数,并且函数是跟计算属性一样,需要return
bigSum(state) {
return state.sum * 10
}
}
- 注册getters
//创建并且暴露 store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
3.2、使用getters中的属性
<h1>当前求和的10倍为:{{ $store.getters.bigSum }}</h1>
4、mapState的引入
4.1、问题
<h1>当前求和为:{{ $store.state.sum }}</h1>
<h1>当前求和的10倍为:{{ $store.getters.bigSum }}</h1>
<h1>我在{{ $store.state.school }}学习{{ $store.state.subject }}</h1>
<!--
在这三段代码中,这个$store.state经常被重复使用,导致表达式的内容过长
-->
4.2、使用计算属性
//使用computed计算属性来实现这个功能
//问题:我们发现,这个he、xuexiao、xueke这些函数也出现重复的请求,所以我们可以通过mapState告诉它he、sum让它自动生成代码就可以解决这个问题
computed: {
he() {
return this.$store.state.sum
},
xuexiao() {
return this.$store.state.school
},
xueke() {
return this.$store.state.subject
},
bigSum() {
return this.$store.getters.bigSum
}
},
<h1>当前求和为:{{ he }}</h1>
<h1>当前求和的10倍为:{{ bigSum }}</h1>
<h1>我在{{ xuexiao }}学习{{ xueke }}</h1>
4.3、使用mapState解决问题
- 第一步:引入mapState
import {mapState} from 'vuex'
- 第二步:调用mapState函数并且将返回值存入计算属性中
computed: {
//需要参数对象,返回的也是对象,然后将返回的数据放入到computed中。 ...的意思是将这个对象中的每一个数据,放入到computed中
...mapState({he: 'sum', xuexiao: 'school', xueke: 'subject'}),
//方式二(数组写法):取数据的时候也要根据sum、school、subject获取
...mapState(['sum','school','subject'])
},
- 第三步:可以使用你定义的key值取到对于的值
<h1>当前求和为:{{ he }}</h1>
<h1>当前求和的10倍为:{{ bigSum }}</h1>
<h1>我在{{ xuexiao }}学习{{ xueke }}</h1>
5、mapGetters的引入
- mapGetters跟mapState用法一致
6、mapMutations的引入
- 解决直接与mutations对话的方法
6.1、引出问题
//调用commit是直接与mutations直接对话,但是存在代码重复的部分
//我们需要传入函数名称、操作类型;当然在点击事件的时候需要传入参数
methods: {
increment() {
this.$store.commit('JIA', this.number)
},
decrement() {
this.$store.commit('JIAN', this.number)
}
}
6.2、mapMutations解决问题
//引入mapMutations
import {mapState, mapGetters, mapMutations} from 'vuex'
methods: {
...mapMutations({increment: 'JIA', decrement: 'JIAN'}),
},
<!--
我们必须在这里传递参数,不然没有办法给生成的函数传递我们想给的参数
-->
<button @click="increment(number)">+</button>
<button @click="decrement(number)">-</button>
7、mapActions的引入
- mapActions直接跟action对话,跟mapMutations的用法一致
8、总结mapxxxx
- mapState和mapGetters是存放到组件的computed属性当中的
- mapActions和mapMutitions是存放到组件的methods属性当中的
9、vuex模块化
9.1、问题-以state为例
//对于我们的sum、school、subject是操作求和模块的,对于personList是操作人员模块的,当我们都将它写在state中,难免会混乱,所以出现vuex的模块化
const state = {
sum: 0,
school: '尚硅谷',
subject: '前端',
personList: [
{id: '001', name: '张三'},
{id: '002', name: '李四'},
]
}
//所以其它的配置actions、mutitions、getters也会出现同样的问题
9.2、对store/index.js进行改造–分模块
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex)
//求和方面的配置项
const countOptions = {
//为了方便mapState通过类型获取
namespaced:true,
actions: {
jiaOdd(miniStore, value) {
//这里面可以写一些业务逻辑,比如ajax请求等等
if (miniStore.state.sum % 2) {
miniStore.commit('JIA', value)
}
},
jiaWait(miniStore, value) {
setTimeout(() => {
miniStore.commit('JIA', value)
}, 500)
}
},
mutations: {
JIA(state, value) {
state.sum += value
},
JIAN(state, value) {
state.sum -= value
}
},
state: {
sum: 0,
school: '尚硅谷',
subject: '前端',
},
getters: {
bigSum(state) {
return state.sum * 10
}
}
}
//人员方面的配置项
const personOptions = {
namespaced:true,
actions: {},
mutations: {
ADD_PERSON(state, personObj) {
state.personList.unshift(personObj)
}
},
state: {
personList: [
{id: '001', name: '张三'},
{id: '002', name: '李四'},
]
},
getters: {}
}
export default new Vuex.Store({
modules: {
countOptions,
personOptions
}
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ujoz6et7-1691737144979)(C:\Users\ASUS\Desktop\java\20_vue\image\1688397137959.png)]
9.3、如何获取和使用
- 简写方式的使用
methods: {
//countOptions下的JIA和JIAN,注意如果使用这种格式,需要添加namespaced配置为true
...mapMutations('countOptions',{increment: 'JIA', decrement: 'JIAN'}),
...mapActions('countOptions',{incrementOdd: 'jiaOdd', incrementWait: 'jiaWait'})
},
computed: {
...mapState('countOptions', ['sum', 'school', 'subject']),
...mapState('personOptions', ['personList']),
...mapGetters('countOptions',['bigSum'])
},
- 原生写法
methods: {
addPerson() {
if (this.name === '') {
alert("输入框不能为null")
return
}
const personObj = {id: nanoid(), name: this.name}
//这里需要使用 / 来往下寻找
this.$store.commit('personOptions/ADD_PERSON', personObj)
this.name = ''
}
},
9.4、严格模块化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1tjC6KJq-1691737144979)(C:\Users\ASUS\Desktop\java\20_vue\image\1688398137646.png)]
sum’, ‘school’, ‘subject’]),
…mapState(‘personOptions’, [‘personList’]),
...mapGetters('countOptions',['bigSum'])
},
- 原生写法
```javascript
methods: {
addPerson() {
if (this.name === '') {
alert("输入框不能为null")
return
}
const personObj = {id: nanoid(), name: this.name}
//这里需要使用 / 来往下寻找
this.$store.commit('personOptions/ADD_PERSON', personObj)
this.name = ''
}
},
9.4、严格模块化
[外链图片转存中…(img-1tjC6KJq-1691737144979)]