Vue从甜小白到皮大佬系列(八) Vuex

8,049 阅读5分钟

🎉 Vue构建大型单页面电商应用 开源啦!点我看源码🚀🚀

阅读时间预计8分钟,读完本文你将收获如下知识点,记得点赞👍哦~

一、啥是Vuex?

Vuex是用来管理Vue的所有组件状态,说白了就是组件中通信的一种高级方式。

Vuex官方文档已经讲解的非常详细,建议通读一遍,本文是自己的学习总结实践和归纳,有些概念和语句借鉴官方。

二、为什么使用Vuex?

在之前的文章组件通信中我们讲了组件中的常用通信方式有props,$emit/v-on,$parent / $children & $refs这三种,他们的局限性在于只能在父子组件中通信,不能在兄弟组件中通信,另外两种是Bus中央总线发布订阅模式可以在任意组件之间进行通信,但是如果在大型项目中难免会有些许瓶颈,组件之间的耦合度较高,那么这时Vuex就恰到好处的出现了,他能够合理的管理各组件中的状态(通信,方法捕获实现,数据传递),将状态从组建中剥离开来,定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,这样我们的代码将会变得更结构化且易维护。

三、Vuex核心

Vuex提供一个单例Store它就相当于一个容器一样,包含着你应用中的state公共数据,我们通过单例Store来统一的按照既定规则来动态的管理这些state,这就是Vuex的核心。

1.State

state就是一个纯对象,上面有一些状态挂载,而且一个应用应该只有一个statestore.js

export default new Vuex.Store({
 state : {//初始状态
    name:'极客James', 
    height:180,
    age:27
  } 
})

1.1mapState:

使用 mapState 辅助函数帮助我们生成计算属性,在需要通信的组件中通过import {mapState} from 'vuex'引入辅助函数,然后要在计算属性computed钩子里面进行数据的映射。

mapState辅助函数使用的三种方式:

  • 第一种:计算属性通过箭头函数取返回值
computed:mapStates({
    name:state=>state.name,
    height:state=>state.name,
    age:state=>state.age
})
  • 第二种方法:利用ES6的...延展特性 (推荐做法)
computed:{
    ...mapState([
        'name',
        'height',
        'age'
    ])
}
  • 第三种方法:利用ES6的...延展特性通过对象取值
computed:{
    ...mapState({
        name:state=>state.name,
        height:state=>state.height,
        age:state=>state.age
    })
}

在组件中取数据

<template>
  <div class="app">
    <div>我是store里面的数据姓名:{{name}}</div>
    <div>我是store里面的数据年龄:{{age}}</div>
    <div>我是store里面的数据身高:{{height}}</div>
  </div>
</template>

输出结果:

2.Getter

可以认为是 store 的计算属性,就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

2.1mapGetter

mapGetter是getter的一个辅助函数,使用方法和mapState类似,推荐使用延展...来取值.

3.Mutation

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,同步改变state的唯一方法。

在组件中通过事件方法来进行同步改变store中的state,需要通过commit提交来响应mutation中的事件方法。

methods: {
add () {
  Store.commit('increment', { number: 10 });
},
reduce () {
  Store.commit('reduce');
}
}

另外一种写法:对象风格的提交方式

推荐使用常量替代 Mutation 事件类型

还记得在组件通信中讲到的发布订阅方法进行组件间通信,使用常量来定义事件类型吗?在Vuex中也是非常推荐使用此办法的,将mutation中的事件常量整合到一个文件夹mutation-type.js中,通过export方式导出.

mutation-type.js

export const ADD = 'ADD'
export const REDUCE = 'REDUCE'

store.js中引入import {ADD,REDUCE} from './mutation-type.js'

import {ADD,REDUCE} from './mutation-type.js'
mutation:{
    [ADD](state,payload) {
        state.age += payload.number;
    },
    [REDUCE](state,payload) {
        state.age --;
    }
}

同样的在需要的组件中引入import {ADD,REDUCE} from './mutation-type.js'

3.1 mapMutation

mapMutationmutation的辅助函数,和state类似都可以通过...扩展运算来进行直接取值。

4.Action

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

注册一个简单的Action,并且从外界传值。

Action 通过 store.dispatch方法触发,在Vue组件中:

另外的写法:参数解构

add({commit}) {
    commit('add');
}

4.1 mapAction

mapActionaction的辅助函数,便于在Vue组件中调用action里面的事件. 在Vue组件中,可以通过解构mapAction的方式将映射

 methods: {
     ...mapActions([
      'add',
      "reduce"
    ])
}

注意此时:用mapActiono映射方法后需要传参那么需要写在事件触发的地方上

<button @click="add({number:10})">增加年龄</button>

4.2 组合 Action

组合Action其实就是异步处理多个commit事件,可以相互调动action,借助Promise保证了数据状态的稳定性和有序性。

官方实例:

store.js

在Vue组件中:

store.dispatch('actionA').then(() => {
 
})

在另外一个 action 中可以异步提交事件:

actions: {
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

推荐阅读:

MDN Promise

async / await

5.Module

当应用变得非常复杂时,store 对象就有可能变得相当臃肿,为了解决这一问题,我们可以将store中的五种状态剥离成单个的模型,然后通过一个入口引入.一般在项目开发中在src目录下创建一个store的目录然后创建以下文件

index.js中引入所有文件,统一挂载在Vuex实例上

main.js中引入index.js并挂载到实例Vue上

常用项目结构:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── state.js          # 根级别的 state
    ├── actions.js        # 根级别的 action
    ├── getter.js         # 根级别的 getter
    ├── mutations.js      # 根级别的 mutation
    ├── mutations-type.js # mutation事件常数名
    └── modules
        ├── a.js       # a模块
        └── b.js       # b模块
        └── ...
    

扫一扫下面的二维码,回复学习即可免费领取最新前端学习资料,也希望在前端进阶的路上,我们一起成长,一起进步!