3) Vuex
1. 入门
学习前先看这个需求,我先在有两个组件,一个是外面的主页,一个是里面的p1,p1里有个文本框,需求是在文本框里修改用户姓名,修改完后希望修改后的姓名显示到主页那里,欢迎xxx:
这个需求如何实现呢,用以前的办法就不好实现了,以前的办法是你这个文本框是属于p1组件的,只能和p1组件里的data()返回的数据对象进行绑定,没办法直接被主页这个组件访问到。
根据这个需求,我们就需要学习一个新的技术了——vuex。
这个问题本质上是一个数据共享的问题,我们可以给这两个组件找一个可以数据共享的地方,一个组件往里存数据,另一个从里面取数据。
有人可能说了,我们可以用上面刚刚学到的localStorage
和sessionStorage
,p1组件点击确定之后,可以将用户姓名存到比如说sessionStorage
里,再让主页从里面取出修改后的用户名不就可以啦?
vuex 可以在多个组件之间共享数据,并且共享的数据是【响应式】的,即数据的变更能及时渲染到模板。
- 与之对比 localStorage 与 sessionStorage 也能共享数据,但缺点是数据并非【响应式】
与vuex相关的代码在src/store/index.js
里:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
首先需要定义 state 与 mutations 他们一个用来读取共享数据,一个用来修改共享数据
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 这里我们就可与来定义共享数据
/*
读取数据,走 state, getters
修改数据,走 mutations, actions
*/
// 从state读取共享数据,在mutations修改共享数据
export default new Vuex.Store({
// 共享数据的位置
state: {
name: '',
},
getters: {
},
// 如果需要修改共享位置,不可以直接修改state里的内容,需要在mutations里添加方法
mutations: {
updateName(state, newName) {
state.name = newName;
},
},
actions: {
},
modules: {
}
})
修改共享数据
代码语言:javascript复制<template>
<div class="p">
p1
<el-input placeholder="请修改用户名" v-model="name"></el-input>
<el-button type="primary" v-on:click="updateName()">修改</el-button>
</div>
</template>
<script>
export default {
data() {
return {
name: ''
}
},
methods: {
updateName() {
// 修改共享数据
// mutations 方法不能直接调用,只能通过 `store.commit(mutation方法名, 参数)` 来间接调用
this.$store.commit('updateName', this.name);
},
}
}
</script>
<style scoped>
...
</style>
- mutations 方法不能直接调用,只能通过
store.commit(mutation方法名, 参数)
来间接调用
读取共享数据
代码语言:javascript复制<template>
<div class="container">
主页
<el-container>
<el-header>
<div class="t">欢迎您:{{this.$store.state.name}}</div>
</el-header>
<el-container>
<el-aside width="200px"></el-aside>
<el-main>
<!-- 占位 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
data() {
return {}
},
}
</script>
<style scoped>
...
</style>
2. mapState
作用:根据state里的属性,自动生成计算属性 name(){ return this.$store.state.name }
每次去写 $store.state.name
这样的代码显得非常繁琐,可以使用计算属性来简化代码:
<template>
。。。
<div class="t">欢迎您:{{name}}</div>
。。。
</template>
<script>
export default {
data() {
return {}
},
computed: {
name() {
return this.$store.state.name
},
}
}
</script>
<style scoped>
...
</style>
如果我们还有一个age数据需要共享,那么还需要在computed
里写一个age()
...
可以用 vuex 帮我们生成计算属性:
src/views/example16/ContainerView.vue
...
<script>
import { mapState } from "vuex"
console.log(mapState(['name', 'age']))
....
可以看到mapState为我们已经生成了对应计算属性了,那么就不需要在computed
里写计算属性了:
<template>
<div class="container">
主页
。。。
<div class="t">欢迎您:{{name}}{{age}}</div>
。。。
</template>
<script>
import { mapState } from "vuex"
export default {
data() {
return {}
},
// computed: {
// name() {
// return this.$store.state.name;
// },
// age() {
// return this.$store.state.age;
// }
// }
computed: mapState(['name', 'age']),
// 等价的写法:... 展开运算符,相当于展开后又塞入了一个空对象里
// computed: {
// ...mapState(['name', 'age'])
// }
}
- mapState方法 返回的是一个对象,对象内包含了 name() 和 age() 的这两个方法作为计算属性
3. mapMutations
作用: 根据
mutatinos
里的属性方法,生成类似于这种形式的代码: updateName() { this.$store.commit('updateName', this.name); },
刚才我们使用了vuex给我们提供的mapState函数帮我们生成了计算属性,算是对我们读取共享数据的一种优化。
那我们来看修改共享数据,原本是我们自己写了个updataName方法来修改共享数据,那么可以可以也有优化呢?
答案是可以的,也可以让vuex帮我们生成好:
代码语言:javascript复制<script>
import { mapMutations } from 'vuex'
// mapMutations()
console.log(mapMutations(['updateName']))
代码语言:javascript复制<template>
<div class="p">
p1
<el-input placeholder="请修改用户名" v-model="name"></el-input>
<!-- 注意,这里的事件要绑定mapMutations生成的方法名,且需要传入一个参数(作为修改的值) -->
<el-button type="primary" v-on:click="updateName(name)">修改</el-button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
data() {
return {
name: ''
}
},
mounted: function () {
this.name = this.$store.state.name;
},
methods: {
// updateName() {
// // 修改共享数据
// // mutations 方法不能直接调用,只能通过 `store.commit(mutation方法名, 参数)` 来间接调用
// this.$store.commit('updateName', this.name);
// },
...mapMutations(['updateName'])
}
}
</script>
<style scoped>
.p {
background-color: rgb(165, 150, 242);
width: 80%;
height: auto;
margin: 50px;
}
</style>
mapMutations
返回的对象中包含的方法,就会调用store.commit()
来执行mutation
方法- 注意参数传递略有不同
4. actions
https://www.bilibili.com/video/BV1Tt4y1772f/?p=94&spm_id_from=pageDriver&vd_source=75d41aa86db8ae1e26da90b1ac6488db&t=38.2
mutations
方法内不能包括修改不能立刻生效(
例如:
代码语言:javascript复制 mutations:{
async updateServerUser(state){
const resp = await axios.get('/api/user');
const {name,age} = resp.data.data;
state.name= name;
state.age = age;
}
}
错误的用法,如果在mutations方法包含了异步操作,会造成开发工具的不准确
)的代码,否则会造成 Vuex 调试工具工作不准确,必须把这些代码写在 actions 方法中
代码语言:javascript复制 import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
/*
读取数据,走 state, getters
修改数据,走 mutations, actions
*/
import axios from '@/util/myaxios'
export default new Vuex.Store({
state: {
name: '',
age: 18
},
getters: {
},
mutations: {
updateName(state, name) {
state.name = name;
},
// 错误的用法,如果在mutations方法中包含了异步操作,会造成开发工具不准确
/* async updateServerName(state) {
const resp = await axios.get('/api/user');
const {name, age} = resp.data.data;
state.name = name;
state.age = age;
} */
updateServerName(state, user) {
const { name, age } = user;
state.name = name;
state.age = age;
}
},
// 拿到响应之后,再间接
actions: {
async updateServerName(context) {
const resp = await axios.get('/api/user');
// 通过commit来定执行那个mutaition
// 参数1:要执行的mutation的名字
// 参数2:数据
context.commit('updateServerName', resp.data.data)
}
},
modules: {
}
})
- 首先应当调用 actions 的 updateServerName 获取数据
- 然后再由它间接调用 mutations 的 updateServerName 更新共享数据
页面使用 actions
的方法可以这么写
<template>
<div class="p">
<el-button type="primary" size="mini"
@click="updateServerName()">从服务器获取数据,存入store</el-button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
const options = {
methods: {
...mapActions(['updateServerName'])
}
}
export default options;
</script>
- mapActions 会生成调用 actions 中方法的代码
- 调用 actions 的代码内部等价于,它返回的是 Promise 对象,可以用同步或异步方式接收结果 // 不使用mapActions的写法: export default { methods:{ updateServerName(){ // this.$store.dispatch("action名称"[,参数]); this.$store.dispatch("updateServerName"); }, }, }
总结: 立刻生效的数据才可以使用mutation修改,不是立刻能获取到的数据,就需要使用actions中转一下,间接调用mutations。
5. 小结
vuex说白了就是在组件之间共享数据的,他共享的数据有一个特点,是响应式的;
- 读取数据:
- 访问store的state属性,可以使用mapState帮我们生成一些计算属性 import {mapState} from 'vuex'
- 访问store的getter属性,和上面这个差不多。
- 修改数据:
- 使用mutations: 可以使用mapMutations替我们生成一些方法,通过调用这些方法,去修改state里的数据 import {mapMutations} from 'vuex'
- 使用actions: 可以使用mapActions替我们生成一些方法,通过调用这些方法,间接的调用mutations里的方法,从而修改state里的数据。 import {mapActions} from 'vuex'
- 具体使用那种,就看你这个数据是否包含了异步操作,没有包括就使用mutation,包括就使用actions。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!