一篇文章快速通关 Vuex(适合小白学习)

发布时间:2024年01月02日

Vue 作为一个渐进式的框架,在学习完主体的部分后,我们可以在用到某个技术的时候再去学别的技术,比如今天的 Vuex 就是一个管理全局数据的技术,主要是要掌握 Vuex 的五大核心,希望这篇文章结合官方文档可以帮助大家更快的掌握 Vuex 的基本使用

Vuex

Vuex 官方文档

01. Vuex 概述

1.1 为什么需要 Vuex?

学习一个工具之前先要了解它能为我们解决问题或者使用它有什么优势

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

状态 就是我们说的 数据,可以帮助我们数据的全局共享,所以一般用来管理一些通用的数据,比如 个人登录信息数据 等。

什么是状态管理模式呢?

比如说我们有一个计数器,点击 + 使得数字自增,- 相反,那这个简单的应用包含哪些部分呢?

  1. 状态:驱动应用的数据源
  2. 视图:展示数据的 状态
  3. 操作:用户的影响数据 状态 变化的操作

这是单一组件的情况,但如果出现多个组件共享状态的时候,就会出现多个视图依赖于统一个状态,多个行为又要变更同一个状态,这样代码写起来就会过于混乱和复杂。

所以考虑到把组件的共享状态单独 抽取 出来,使得组件树的任何地方都能获取状态和通过行为改变状态。

在这里插入图片描述

使用 Vuex 可以实现数据的 集中化管理,并且由于其内部提供的辅助函数,操作简洁

1.2 注意

并不是所有情况都需要用到 Vuex,我们来看一下官方的解释

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发 大型单页 应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它。

02. Vuex 安装与基础使用

2.1 安装

因为我们目前使用 Vue CLI 架子来初始化项目的时候选中了vuex就不需要再手动导入了,但是如果没有选择 就需要额外安装:

vue3

yarn add vuex@next --save 或者 npm install vuex@next --save

然后我们来新建一个 stroe 目录来放置一个 index.js 文件,后面我们基于 Vue CLI 选单中 vuex 后生成的初始化项目来讲述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在 WebStrom 中打开项目

2.2 基础代码

每一个 Vuex 应用的核心就是 store(仓库)。stor”是一个容器,它包含着你的应用中大部分的状态 (state)

  1. 新建仓库(store/index.js

    import { createStore } from "vuex";
    
    export default createStore({
      state: {},
      getters: {},
      mutations: {},
      actions: {},
      modules: {},
    });
    
  2. store 对象作为插件安装(main.js

    import { createApp } from 'vue'
    import App from './App.vue'
    import store from './store'
    
    createApp(App).use(store).mount('#app')
    
    

这样就完成了一个仓库的创建,也是 Vue CLI 初始化帮助我们搭建好的模板。

03. 核心概念 - state 状态

创建完仓库后,我们就要考虑如何给仓库 提供 数据和如何 使用 仓库中的数据了

3.1 提供数据

我们可以向参控股中的 state 对象去添加一份数据

import { createStore } from 'vuex'

export default createStore({
  // 通过 state 可以提供数据
  state: {
    count: 101
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

比如上面的情况,我们就向里面添加了一个 count 数据

3.2 使用数据(通过 store 直接访问)

  1. 通过 store 来直接访问
  2. 借助辅助函数

获取 store 的两种方式

this.store
import store from "@/store/index";

导入后应该如何使用呢?

再模板中: {{ $store.state.xxx }}

在组件函数中: this.$store.state.xxx

在 JS 模块中: store.state.xxx

比如我们可以通过上面的方式在 App.vue 组件中去访问到上面的数据:

App.vue

<template>
  <h1>{{store.state.count }}</h1>
</template>

<script>
import store from "@/store/index";

export default {
  name: 'App',
  data() {
    return {store}
  },
  components: {}
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

3.3 使用数据(辅助函数)

通过 mapState 函数将 store 中的数据自动映射到组件的 计算属性

  1. 导入 import {mapState} from ;vuex
  2. 数组方式引入 state mapState(['count']) 数组中写需要的属性
  3. 展开运算符映射 ...mapState(['count'])

得到的是一个对象,对象中包含着计算属性,我们返回的结果赋值给 computed 即可,但是出现的问题就是我们的 computed 直接就被占满了,如果我们还想添加新的计算属性就没法操作了,所以我们就把这个数据展开最终的代码就是这样的

<template>
  <h1>{{ count }}</h1>
</template>

<script>
import store from "@/store/index";
    // 引入
import {mapState} from "vuex";

export default {
  name: 'App',
  data() {
    return {store}
  },
  computed:
      {
          // 展开运算符 
        ...mapState(['count'])
      }
}
</script>

04. 核心概念 - mutations

4.1 单向数据流

vuex 中同样遵循单向数据流,组件中不能直接修改仓库中的数据,比如我们试一下

<template>
  <h1>{{ count }}</h1>
  <button @click="handleAdd">+</button>
</template>

<script>
import store from "@/store/index";
import {mapState} from "vuex";

console.log(mapState(['count', 'title']))
export default {
  name: 'App',
  methods: {
    handleAdd() {
      this.count++;
    }
  },
  computed:
      {
        ...mapState(['count'])
      }
}
</script>

这里我们注册了一个按钮来使得数据自增,经过试验是无法操作的,试想一下,如果任意位置都能修改仓库的数据,那我们后续去维护的时候,就很难找到数据是在哪个组件去修改的,所以需要用到 mutations.

4.2 mutations 操作流程

  1. store 定义的位置定义一个 mutations 对象,内部存放修改 state 的方法

    mutations: {
        addCount (state) {
           state.count += 1;
        }
    }
    
  2. 组件中提交调用 mutations

    this.$store.commit('addCount')
    

下面给出代码示例

index.js

import { createStore } from 'vuex'

export default createStore({
  // 通过 state 可以提供数据
  state: {
    count: 101,
    title: '仓库标题'
  },
  getters: {
  },
  // 提供修改数据的方法
  mutations: {
    addOne(state) {
      state.count += 1;
    }
  },
  actions: {
  },
  modules: {
  }
})

App.xue

  methods: {
    handleAdd() {
      // 需要提交调用 mutations
      this.$store.commit('addOne');
    }
  },

这样就是实现了操控 store 中的内容

4.3 mutations 传参语法

如果我们想要实现自己控制加的数字大小,只需要传参的时候传入需要加的参数即可,mutations 也提供了传参的语法

  1. 提供 mutation 函数,带参数的

    mutations: {
        addCount (state, 参数) {
           state.count += 参数;
        }
    }
    
  2. 页面中提交调用 mutation

    this.$store.commit('addCount', 参数)
    

下面给出代码示例

index.js

import { createStore } from 'vuex'

export default createStore({
  // 通过 state 可以提供数据
  state: {
    count: 101,
    title: '仓库标题'
  },
  getters: {
  },
  // 提供修改数据的方法
  mutations: {
    addOne(state, n) {
      state.count += n;
    }
  },
  actions: {
  },
  modules: {
  }
})

App.xue

  methods: {
    handleAdd() {
      // 需要提交调用 mutations
      this.$store.commit('addOne', 10);
    }
  },

4.4 mapMutations 函数

这个函数和前面的 mapstate 很像,它是把位于mutataions 中的方法提取出来,映射到组件 methods 中,这里直接给出示例代码:

<template>
  <h1>{{ count }}</h1>
  <button @click="addNum(10)">+</button>
</template>

<script>
import {mapState} from "vuex";
import {mapMutations} from "vuex";

console.log(mapState(['count', 'title']))
export default {
  name: 'App',
  // 引入方法
  methods: {
    ...mapMutations(['addNum'])
  },
  computed:
      {
        ...mapState(['count'])
      }
}
</script>

05. 核心概念 - actions 与 getters

5.1 actions 应用

action 是为了让我们可以异步的操控这个数据,之前学到的 mutations 中的方法为了监测必须是同步的,所以如果需要异步的方法就需要 actions

  1. 提供 actions 方法
actions: {
	setAsyncCount (content, num) {
		setTimeout(() => {
			context.commit('changeCount', num);
		}, 1000);
	}
}

可以看到, actions 中的方法仍然使用了 commit 去修改,只是可以把异步的代码写到里面

  1. 在页面中 dispatch 调用
this.$store.dispatch('setAsyncCount', 200);

5.2 辅助函数 mapActions

和上面的 mapMutations 非常类似,是吧位于 actions 中的方法提取出来,映射到组件 methods 中.

index.js

import {createStore} from 'vuex'

export default createStore({
    // 通过 state 可以提供数据
    state: {
        count: 101,
        title: '仓库标题'
    },
    getters: {},
    // 提供修改数据的方法
    mutations: {
        addNum(state, n) {
            state.count += n;
        }
    },
    actions: {
        changeNum(context, num) {
            setTimeout(() => {
                context.commit('addNum', num)
            }, 1000);
        }
    },
    modules: {}
})

App.vue

<template>
  <h1>{{ count }}</h1>
  <button @click="addNum(10)">+</button>
  <button @click="changeNum(10)">一秒后加</button>
</template>

<script>
import {mapActions, mapState} from "vuex";
import {mapMutations} from "vuex";

console.log(mapState(['count', 'title']))
export default {
  name: 'App',
  // 引入方法
  methods: {
    ...mapMutations(['addNum']),
    ...mapActions(['changeNum'])
  },
  computed:
      {
        ...mapState(['count'])
      }
}
</script>

5.3 getters 应用

除了 state 以外,我们还可能需要用到基于 state 中的内容的属性的新属性(和前面学到的计算属性非常类似).

  1. 定义 getters: 下面演示了 state 中的 list 数组中保存数据,我们提取出其中大于 5 的部分作为计算属性

    getters: {
        filterList (state) {
        	return state.list.filter(item => item > 5)
        }
    }
    
  2. 访问 getters

    $store.getters.filterList
    
  3. 同样的,也可以通过 mapGetters 映射

    computer: {
    	...mapGetters(['filterList'])
    }
    

06. 核心概念 - module(进阶语法)

6.1 基本介绍

由于 vuex 使用单一状态树,所有的状态都会集合到这个很大的 state 对象中,如果要存储的内容非常多,这个对象就会非常臃肿,可维护性和可读性都非常差,所以为什么不分模块呢?

那拆分成模块需要哪些步骤呢?

  1. store 下面新建一个 modules 文件夹
  2. 在这个文件夹下面编写 js 代码,每个代码就代表一个模块
  3. 在主文件中引入模块,和我们之前引入 store 类似

user.js

const state = {
    userInfo: {
        name: 'Tom',
        age: 18
    }
}
const mutations = {}
const actions = {}
const getters = {}
export default {
    state,
    mutations,
    actions,
    getters
}

index.js

import {createStore} from 'vuex'
import user from "./modules/user";
import setting from "./modules/setting";

export default createStore({
    // 通过 state 可以提供数据
    state: {
        count: 101,
        title: '仓库标题'
    },
    getters: {},
    // 提供修改数据的方法
    mutations: {
        addNum(state, n) {
            state.count += n;
        }
    },
    actions: {
        changeNum(context, num) {
            setTimeout(() => {
                context.commit('addNum', num)
            }, 1000);
        }
    },
    modules: {
        user,
        setting
    }
})

注意看上面的 modules 部分

6.2 使用子模块中的数据

虽然已经分模块了,但本质上还是挂载到跟模块上的,但是管理和维护起来变得非常容易了

下面给出如何访问子模块中的数据,因为和前面非常类似,就不写案例了

使用子模块中的数据:

  1. 直接通过模块名访问 $store.state.模块名.xxx
  2. 通过 mapState 映射:
    1. 默认根级别的映射 mapState([ ‘xxx’ ])
    2. 子模块的映射 :mapState(‘模块名’, [‘xxx’]) - 需要开启命名空间

获取 getter 数据:

使用模块中 getters 中的数据:

  1. 直接通过模块名访问 $store.getters['模块名/xxx ']
  2. 通过 mapGetters 映射
    1. 默认根级别的映射 mapGetters([ 'xxx' ])
    2. 子模块的映射 mapGetters('模块名', ['xxx']) - 需要开启命名空间

获取 mutations 方法:

  1. 直接通过 store 调用 $store.commit('模块名/xxx ', 额外参数)
  2. 通过 mapMutations 映射
    1. 默认根级别的映射 mapMutations([ ‘xxx’ ])
    2. 子模块的映射 mapMutations(‘模块名’, [‘xxx’]) - 需要开启命名空间

获取 actions 方法:

  1. 直接通过 store 调用 $store.dispatch('模块名/xxx ', 额外参数)
  2. 通过 mapActions 映射
    1. 默认根级别的映射 mapActions([ ‘xxx’ ])
    2. 子模块的映射 mapActions(‘模块名’, [‘xxx’]) - 需要开启命名空间

开启命名空间:

写在子模块的导出语句中:

const state = {
    userInfo: {
        name: 'Tom',
        age: 18
    }
}
const mutations = {}
const actions = {}
const getters = {}
export default {
    namespaced: true,
    state,
    mutations,
    actions,
    getters
}
文章来源:https://blog.csdn.net/weixin_74895237/article/details/135330556
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。