4.3 Vuex
4.3.1 Vuex是什么
与我们vue3匹配的版本是 vuex4。
Vuex是一个专门为Vue.js应用程序开发的状态管理模式+库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
4.3.1.1 什么是“状态管理模式”
举一个简单的例子,在页面上写一个输入框,它是属于视图view,我们进行输入是一个操作actions,这样会改变数据的状态state。这就是一个简单的单向数据流。但是,当我们遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。这个时候vuex闪亮登场。
4.3.1.2 什么情况下我应该使用Vuex
如果我们的项目是一个简单的单页应用,使用Vuex可能会让我们写起来更加复杂。如果我们的项目本身很复杂,那么使用Vuex就会让我们的灵活性、可扩展性提高。
4.3.2 安装
在html里面使用引入CDN的方式:
<script src="https:unpkg.com/vuex@4.0.0/dist/vuex.global.js"></script>
安装方式:
npm install vuex@next --save
yarn add vuex@next --save
4.3.3 简单的创建
安装好Vuex,用它的createStore来创建一个store实例。给它提供一个初始的state对象和一些mutation(引发state变化),在main.js中实践一下吧:
import { createStore } from 'vuex'
const store = createStore({
state(){
return {
count: 0
}
},
mutations:{
add(state){
state.count++
}
}
})
//挂到App根组件上
createApp(App).use(store).mount('#app')
这样,在所有组件中都能通过store.state来获取状态对象,并通过store.commit方法触发状态变更:
store.commit('add') //里面写的是我上方定义的mutation里的函数
console.log(store.state.count) //打印出来就是 1
在Vue组件中,可以通过this.$store访问store实例。现在我们可以从组件的方法提交一个变更:
methods:{
add(){
this.$store.commit('add')
console.log(this.$store.state.co)
}
}
同样的,我们最好另外建一个store.js文件夹,专门来维护store,然后再把它引入到main.js注入全局就可以了,以后都这么做。
4.3.4 核心概念
4.3.4.1 State
简单地说,state就是用来存数据的。
它有一个重要的辅助函数mapState
回顾我们在组件中使用store数据的方式,是$store.state.xxx,那如果我在页面中要多次使用这个数据,岂不是得写很多次?
<template>
<h1>{{$store.state.xxx}}</h1>
<h1>{{$store.state.xxx}}</h1>
<h1>{{$store.state.xxx}}</h1>
</template>
这样的模板又长又臭,所以一般来说我们会把它写成计算属性:
<template>
<h1>{{xxx}}</h1>
<h1>{{xxx}}</h1>
<h1>{{xxx}}</h1>
</template>
<script>
export default {
computed:{ //计算属性
xxx(){
return $store.state.xxx
}
}
}
</script>
但这时候又有一个问题,如果说 我们的state里面有很多个数据,那我们就得写很多个计算属性,这时候vuex给我们提供了一个mapState辅助函数。它的作用是能够帮我们生成计算属性,只需要我们传一些东西给它,下面先来介绍三种基础用法,假设我们store中定义了一个变量count:
export default {
data(){
return {
num:1
}
}
computed: mapState({ //写在计算属性里
//1. 箭头函数
count:state => state.count,
//2. 传字符串参数
count: 'count'
//3. 为了能够使用'this'获取局部状态,可以使用常规函数形式,例如这里我要跟this.num互动
countFun(state){
return state.count + this.num
}
})
}
除了以上三种方式呢,还有一种最典型的用法,当映射的计算属性的名称与state的子节点名称相同时,可以给mapState传一个字符串数组,说着很绕,看一看就明白了:
export default {
//假设我们在store中定义了两个变量,一个count,一个name
//直接这样就能拿到,而且在模板中也可直接用
computed: mapState(['count','name'])
}
不仅如此,而且如果我们当前组件还需要其他的计算属性,可以用解构的方式:
export default {
data(){
return {
num:1
}
}
//解构的话computed就需要{}
computed: {
//拿到我们需要的store中的数据
...mapState(['count','name']),
//还可以另外定义计算属性
myNum(){
return this.num++
}
}
}
4.3.4.2 Getter
试想一种情景,有些时候我们需要从store拿到数据,但我们需要的不止是它数据本身而可能是其他的一些东西,例如我们的数据是一个数组,里面数据有true有false,而我们只要true的数据,那是不是需要进行一步筛选。好,那如果只是一个组件里面要筛选还可以接受,那如果是多个组件都需要呢,那么就需要可以复用的数据,getter也就出现了。
const store = createStore({
state(){
return {
count:0,
name:'xxx',
todos: [{do:'唱歌',done:true},{do:'跳舞',done:true},{do:'rap',done:false}]
}
},
getters:{
doneCount(state){ //有两个参数state,getters,这样doneCount()在每个组件都能复用
return state.todos.filter(todo => todo.done===true).length
}
}
})
那具体怎么复用呢,官方提供了一个简便的做法mapGetters。用法跟上面学的mapState()是一样的:
//任意组件中的写法
computed:{
...mapGetters(['doneCount']),
...mapState(['count','name']),
myComp(){ //组件依然可以有自己的计算属性
return xxxx
}
}