从0学习Vue3(11)

98 阅读4分钟

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
     }
 }