Vue2.x 基本认识三:Vuex
认识 Vuex
概念(重要)
专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 Vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,适用于任意组件间通信。
题外话
前面学过组件间通信的方式有:
- props:适用于父传子、子传父,兄弟组件之间不行。
- 全局事件总线($bus):适用于任意组件之间通信。
- 消息发布与定语:适用于任意组件之间通信。
Vuex 其他信息
github地址:https://github.com/vuejs.vuex
Vuex 使用时机
多个组件依赖于同一个状态
来自不同组件需要变更同一状态
Vuex 原理图
安装 Vuex
需要注意,2022年2月7日,Vue3 成为了默认版本,Vuex 也更新到了4版本了。而 Vuex 4 只能在 Vue 3 中使用。使用命令 npm i vuex 默认安装的最新版本,也就是 Vuex 4 版本。Vue 2 只能使用 Vuex 3 版本。
安装命令:npm i vuex@3
搭建 Vuex 环境
创建 js 文件 src\store\index.js(另一种方式是 src\vuex\store.js),并将填入下面代码:
/** * 该文件用于创建 Vuex 中最为核心的 store */ import Vue from 'vue' // 引入 vuex 插件 import Vuex from 'vuex' // 使用 vuex 插件 Vue.use(Vuex) // 准备 actions 用于响应组件中的动作 const actions = {} // 准备 mutations 用于操作数据(state) const mutations = {} // 准备 state 用于存储数据 const state = {} // 创建并导出 store export default new Vuex.Store({ actions, mutations, state })
在 main.js 文件中引入 store.js,并且添加配置项
import Vue from 'vue' import App from "./App.vue" Vue.config.productionTip = false // 引入 store import store from './store' // import store from './store/index.js' 的简写,不指定文件默认就是index const vm = new Vue({ render: h => h(App), // 只有引入 vuex 插件以后,才能使用 store 配置项 store, // store: store 的简写 beforeCreate() { Vue.prototype.$bus = this // 安装全局事件总线 } }).$mount("#root")
Vuex 基本使用
actions 配置项中的方法名使用驼峰命名法;这里的方法中能写通用的业务逻辑代码。
mutations 配置项中的方法名要大写,里面不要写业务逻辑代码。
state 配置项用于存放公共数据
src\store\index.js 文件的 actions、mutations 中定义函数,state 对象中定义公共数据
/** * 该文件用于创建 Vuex 中最为核心的 store */ import Vue from 'vue' // 引入 vuex 插件 import Vuex from 'vuex' // 使用 vuex 插件 Vue.use(Vuex) // 准备 actions 用于响应组件中的动作(业务逻辑写在这,mutations 中就直接做具体操作) const actions = { incrementOdd(context, value) { if (context.state.sum % 2) { context.commit('INCREMENT_FUNC', value) } }, incrementWait(context, value) { setTimeout(()=>{ context.commit('INCREMENT_FUNC', value) }, 500) } } // 准备 mutations 用于操作数据(state) const mutations = { INCREMENT_FUNC(state, value) { state.sum += value }, DECREMENT_FUNC(state, value) { state.sum -= value } } // 准备 state 用于存储数据 const state = { sum:0 // 当前的和 } // 创建并导出 store export default new Vuex.Store({ actions, mutations, state, })
组件中操作上面 actions、mutations定义的方法,以及 state 对象中的公共数据
// template 标签中代码 <!-- 使用 store 中定义的公共数据,在下面 js 代码中使用,前面加 this. 即可 --> <h1>当前求和为:{{ $store.state.sum }}</h1> // script 标签中 Vue 两个配置项中代码 data() { return { n:1 } }, methods: { increment() { // this.$store.commit 直接调用 mutations 中的方法 this.$store.commit('INCREMENT_FUNC', this.n) }, decrement() { this.$store.commit('DECREMENT_FUNC', this.n) }, incrementOdd() { // this.$store.dispatch 调用 actions 中的方法 this.$store.dispatch('incrementOdd', this.n) }, incrementWait() { this.$store.dispatch('incrementWait', this.n) } }
Vuex 的 getters 配置项
src\store\index.js 中追加 getters 配置(getters 中的函数有点类似于计算属性)
......
// 准备 getters 用于加工 state 中的数据(这个配置项中定义的方法,参数就是 state)
const getters = {
bigSum(state) {
return state.sum * 10
}
}
// 创建并导出 store
export default new Vuex.Store({
......
getters
})
组件中使用
// html 中 {{ $store.getters.bigSum }} // js 中 this.$store.getters.bigSum
mapState、mapGetters、mapActions、mapMutations
作用
mapState、mapGetters 用于对 store(src\store\index.js) 里 state、getters 两个配置对象中的属性做映射,自动生成计算属性的函数体内容。
在组件中引入和使用 mapState、mapGetters、mapActions、mapMutations
这里不在提供 store 中共享属性和函数的写法,上面都有
<template> <div class="countRoot"> <!-- 使用 mapState、mapGetters 生成的计算属性 --> <h1>当前求和为:{{ sum }}</h1> <h1>当前求乘10:{{ bigSum }}</h1> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <!-- 使用 mapActions、mapMutations 生成方法无法指定传参,需要在这里传参 --> <button @click="increment(n)">加</button> <button @click="decrement(n)">减</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template> <script> // 引入 mapGetters, mapState,mapMutations,mapActions import { mapGetters, mapState,mapMutations,mapActions } from 'vuex' export default { name: 'Count', data() { return { n:1 } }, computed:{ // 程序员自己编写计算属性代码,取 state 和 getters 中的属性 /* sum() { return this.$store.state.sum } bigSum() { return this.$store.getters.bigSum } */ /* ******************************************************************************************************************** */ /** * 借助 mapState、mapGetters 生成计算属性 */ // 参数为对象的写法。对象中 key 是自定义的计算属性名,value 则必须队友 state 中的属性名(value 注意引号不能像 key 那样省略) ...mapState({sum:'sum'}), // 参数为数组的写法。如果上面对象写法中,key 和 value 想同时,就可以使用这种写法; // 数组元素必须与 state 中属性名一致,默认计算属性名也与 state 中属性名一致。 // ...mapState(['sum']) // mapGetters 用法与 mapState 一致 ...mapGetters(['bigSum']) }, methods: { /** * 程序员自己编写方法调用 mutations、actions 中的方法 */ /* increment() { // this.$store.commit 直接调用 mutations 中的方法 this.$store.commit('INCREMENT_FUNC', this.n) }, decrement() { this.$store.commit('DECREMENT_FUNC', this.n) }, incrementOdd() { // this.$store.dispatch 调用 actions 中的方法 this.$store.dispatch('incrementOdd', this.n) }, incrementWait() { this.$store.dispatch('incrementWait', this.n) } */ /* ******************************************************************************************************************** */ /** * 借助 mapMutations、mapActions 生成方法 * 参数要求与 mapState、mapGetters 一样; * 区别是 mapState、mapGetters 生成的是计算属性,页面是可以直接用; * mapMutations、mapActions 生成的是方法,且在生成时不能指定参数,所以需要在页面上调用方法的时候传参 */ // 对象写法(key 就是组件中使用的方法名,value 对应 mutations、actions 中的方法名) ...mapMutations({increment:'INCREMENT_FUNC',decrement:'DECREMENT_FUNC'}), // 数组写法(用这种方式生成 actions 里面的方法还好,mutations 里面的方法名是大写的,生成以及使用时都需要大写,比较难看) ...mapActions(['incrementOdd', 'incrementWait']) } } </script> <style scoped> .countRoot { background-color: rgb(211, 130, 153); } button,select { background-color:rgb(194, 238, 192); height: 30px; } </style>
Vuex 模块化编码(进阶使用)
概念:将 src\store\index.js 里面 state、getters、actions、mutations 的内容,以组件为单位拆分出多个模块,放在不同的 js 文件中,声明导出方式,然后以模块的形式从新引入到 index.js 文件中。
作用:根据不同的组件或者业务功能拆分成不同模块,主要是为了避免所有东西都写在一起,不便于维护和查看。
新建模块
这里假设需要一个 Count.vue 组件对应的模块,名字叫 count.js。
注意:一定要开启命名空间,否则在使用 mapState、mapGetters、mapActions、mapMutations 简化代码会有大问题。
export default { namespaced: true, // 开启命名空间 // 准备 actions 用于响应组件中的动作(业务逻辑写在这,mutations 中就直接做具体操作) actions: { incrementOdd(context, value) { if (context.state.sum % 2) { context.commit('INCREMENT_FUNC', value) } }, incrementWait(context, value) { setTimeout(()=>{ context.commit('INCREMENT_FUNC', value) }, 500) } }, // 准备 mutations 用于操作数据(state) mutations: { INCREMENT_FUNC(state, value) { state.sum += value }, DECREMENT_FUNC(state, value) { state.sum -= value } }, // 准备 state 用于存储数据 state: { sum:0 // 当前的和 }, // 准备 getters 用于加工 state 中的数据(这个配置项中定义的方法,参数就是 state) getters: { bigSum(state) { return state.sum * 10 } } }
引入模块
在 src\store\index.js 中
/** * 该文件用于创建 Vuex 中最为核心的 store */ import Vue from 'vue' // 引入 vuex 插件 import Vuex from 'vuex' // 使用 vuex 插件 Vue.use(Vuex) // 引入模块化 store 模块化后,count.js 文件 import countOptions from './count' // 创建并导出 store export default new Vuex.Store({ modules: { countAbout: countOptions, // 配置模块 } })
模块化后使用方式
代码中含使用插件简化代码写法,和自己调用 commit、dispatch 方法写法
<template> <div class="countRoot"> <!-- 使用 store 中定义的公共数据 --> <h1>当前求和为:{{ sum }}</h1> <h1>当前求乘10:{{ bigSum }}</h1> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <!-- 使用 mapActions、mapMutations 生成方法无法指定传参,需要在这里传参 --> <button @click="increment(n)">加</button> <button @click="decrement(n)">减</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template> <script> // 引入 mapGetters, mapState,mapMutations,mapActions import { mapGetters, mapState,mapMutations,mapActions } from 'vuex' export default { name: 'Count', data() { return { n:1 } }, computed:{ // 模块化后,程序员自己编写计算属性代码,取 state 和 getters 中的属性 /* sum() { return this.$store.state.countAbout.sum }, bigSum() { return this.$store.getters['countAbout/bigSum'] }, */ /* ******************************************************************************************************************** */ /** * 模块化后,借助 mapState、mapGetters 生成计算属性 */ // 参数为对象的写法。对象中 key 是自定义的计算属性名,value 则必须队友 state 中的属性名(value 注意引号不能像 key 那样省略) // ...mapState({sum:'sum'}), // 参数为数组的写法。如果上面对象写法中,key 和 value 想同时,就可以使用这种写法; // 数组元素必须与 state 中属性名一致,默认计算属性名也与 state 中属性名一致。 ...mapState('countAbout', ['sum']), // mapGetters 用法与 mapState 一致 ...mapGetters('countAbout', ['bigSum']) }, methods: { /** * 模块化后,程序员自己编写方法调用 mutations、actions 中的方法 */ /* increment() { // this.$store.commit 直接调用 mutations 中的方法 this.$store.commit('countAbout/INCREMENT_FUNC', this.n) }, decrement() { this.$store.commit('countAbout/DECREMENT_FUNC', this.n) }, incrementOdd() { // this.$store.dispatch 调用 actions 中的方法 this.$store.dispatch('countAbout/incrementOdd', this.n) }, incrementWait() { this.$store.dispatch('countAbout/incrementWait', this.n) } */ /* ******************************************************************************************************************** */ /** * 模块化后,借助 mapMutations、mapActions 生成方法 * 参数要求与 mapState、mapGetters 一样; * 区别是 mapState、mapGetters 生成的是计算属性,页面是可以直接用; * mapMutations、mapActions 生成的是方法,且在生成时不能指定参数,所以需要在页面上调用方法的时候传参 */ // 对象写法(key 就是组件中使用的方法名,value 对应 mutations、actions 中的方法名) ...mapMutations('countAbout', {increment:'INCREMENT_FUNC',decrement:'DECREMENT_FUNC'}), // 数组写法(用这种方式生成 actions 里面的方法还好,mutations 里面的方法名是大写的,生成以及使用时都需要大写,比较难看) ...mapActions('countAbout', ['incrementOdd', 'incrementWait']) } } </script> <style scoped> .countRoot { background-color: rgb(211, 130, 153); } button,select { background-color:rgb(194, 238, 192); height: 30px; } </style>