当前位置: 首页 > news >正文

2.16Vue全家桶-Vuex状态管理

1.认识应用状态管理

1.1什么是状态管理

在开发中,我们的应用程序需要处理各种各样的数据,这些数据需要保存在我们应用程序中的某一个位置,对于这些数据的管理我们就称之为是状态管理。

在前面我们是如何管理自己的状态呢?

  • 在 Vue 开发中,我们使用组件化的开发方式;
  • 而在组件中我们定义 data 或者在 setup 中返回使用的数据,这些数据我们称之为 state;
  • 在模块 template 中我们可以使用这些数据,模块最终会被渲染成 DOM,我们称之为 View;
  • 在模块中我们会产生一些行为事件,处理这些行为事件时,有可能会修改 state,这些行为事件我们称之为 actions;

1.2复杂的状态管理

JavaScript 开发的应用程序,已经变得越来越复杂了:

  • JavaScript 需要管理的状态越来越多,越来越复杂;
  • 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等;
  • 也包括一些 UI 的状态,比如某些元素是否被选中,是否显示加载动效,当前分页;

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态;
  • 来自不同视图的行为需要变更同一状态;

我们是否可以通过组件数据的传递来完成呢?

  • 对于一些简单的状态,确实可以通过 props 的传递或者 Provide 的方式来共享状态;
  • 但是对于复杂的状态管理来说,显然单纯通过传递和共享的方式是不足以解决问题的,比如兄弟组件如何共享数据呢?

1.3Vuex的状态管理

管理不断变化的 state 本身是非常困难的:

  • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View 页面也有可能会引起状态的变化;
  • 当应用程序复杂时,state 在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪;

因此,我们是否可以考虑将组件的内部状态抽离出来,以一个全局单例的方式来管理呢?

  • 在这种模式下,我们的组件树构成了一个巨大的 “视图 View”;
  • 不管在树的哪个位置,任何组件都能获取状态或者触发行为;
  • 通过定义和隔离状态管理中的各个概念,并通过强制性的规则来维护视图和状态间的独立性,我们的代码会变得更加结构化和易于维护、跟踪;

这就是 Vuex 背后的基本思想,它借鉴了 Flux、Redux、Elm(纯函数语言,redux 有借鉴它的思想);

当然,目前 Vue 官方也在推荐使用 Pinia 进行状态管理,我们后续也会进行学习。

2.Vuex的基本使用

2.1Vuex的手动安装

2.2Vuex的基本使用过程

第一,在文件夹store中创建一个index.js的文件。

第二,1.引入vuex ,2.定义一些公有的状态,3.导出

第三,在main.js中app.use()

第四,直接在组件中进行使用(可以在模板中使用,在js代码中使用)

2.3创建store

  1. Vuex 的核心 - store
    • 每一个 Vuex 应用的核心是 store(仓库),本质上是一个容器,包含应用中大部分的状态(state)。

  2. Vuex 与单纯全局对象的区别

    • 响应式存储:Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态时,若 store 中的状态发生变化,相应的组件也会被更新。

    • 状态改变方式:不能直接改变 store 中的状态,改变 store 中状态的唯一途径是提交(commit)mutation,这样便于跟踪每一个状态的变化,通过工具更好地管理应用的状态。

  3. 使用步骤
    • 创建 Store 对象。
    • 在 app 中通过插件安装。

2.4使用store

3.mapStates映射

3.1在optionAPI中进行状态映射

  1. 在组件中获取状态的优化方式
    • 若觉得之前在组件中获取状态的方式繁琐(表达式过长),可使用计算属性。示例代码如下:

javascript

运行

computed: {counter() {return this.$store.state.counter}
},
  • 若有多个状态需要获取,可以使用 mapState 的辅助函数。
    • mapState 的方式一:对象类型。
    • mapState 的方式二:数组类型。
    • 也可以使用展开运算符和原来的 computed 混合在一起。

3.2在compositionAPI中进行状态映射

  1. 在 setup 中获取状态
    • 单个获取状态很简单,通过 useStore 拿到 store 后去获取某个状态即可。
    • 若需使用 mapState 功能,默认情况下 Vuex 未提供非常方便的使用方式,因此进行了函数封装。
  2. 封装函数代码

javascript

运行

import { useStore, mapState } from 'vuex';
import { computed } from 'vue';export function useState(mapper) {const store = useStore();const stateFns = mapState(mapper)const state = {}Object.keys(stateFns).forEach(fnKey => {state[fnKey] = computed(stateFns[fnKey].bind({ $store: store }))})return state
}
  1. 在 setup 中使用封装函数示例

javascript

运行

setup() {const state = useState({name: state => state.name,age: state => state.age,height: state => state.height})return {...state}
}

3.核心概念State

见2、3

4.核心概念Getters

4.1Getters的基本使用

  1. Vuex 中 getters 的用途:当某些属性需要经过变化后使用时,可以使用 getters。
  2. 代码示例

javascript

运行

const store = createStore({state() {return {counter: 0,name: "coderwhy",age: 18,height: 1.88,books: [{ name: "vuejs", count: 2, price: 110 },{ name: "react", count: 3, price: 120 },{ name: "webpack", count: 4, price: 130 }]}},getters: {totalPrice(state) {let totalPrice = 0;for (const book of state.books) {totalPrice += book.count * book.price}return totalPrice}}
})
  1. 在模板中使用 getters 的示例

html

预览

<div><h2>{{ $store.getters.totalPrice }}</h2>
</div>

4.2Getters的mapGetters映射

4.3Getters第二个参数

4.4getters的返回值

在optionAPI中

在compositionAPI中

5.核心概念Mutations

5.0Mutations的基本使用

5.1Mutations携带参数

很多时候我们在提交 mutation 的时候,会携带一些数据,这个时候我们可以使用参数:

plaintext

mutations: {addNumber(state, payload) {state.counter += payload}
}

payload 为对象类型

plaintext

addNumber(state, payload) {state.counter += payload.count
}

对象风格的提交方式

plaintext

$store.commit({type: "addNumber",count: 100
})

5.2Mutations常量类型

定义常量:mutation - type.js

js

export const ADD_NUMBER = 'ADD_NUMBER'

定义 mutation

js

[ADD_NUMBER](state, payload) {state.counter += payload.count
}

提交 mutation

js

$store.commit({type: ADD_NUMBER,count: 100
})

5.3Mutations的mapMutations映射

我们也可以借助辅助函数,帮我们快速映射到对应的方法中:

js

methods: {...mapMutations({addNumber: ADD_NUMBER,}),...mapMutations(["increment", "decrement"]),
}

在 setup 中使用也是一样的:

js

const mutations = mapMutations(['increment', 'decrement']);
const mutations2 = mapMutations({addNumber: ADD_NUMBER
})

5.4Mutations的重要原则

一条重要的原则就是要记住 mutation 必须是同步函数:

  • 这是因为 devtool 工具会记录 mutation 的日记;
  • 每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照;
  • 但是在 mutation 中执行异步操作,就无法追踪到数据的变化;

所以 Vuex 的重要原则中要求 mutation 必须是同步函数;

但是如果我们希望在 Vuex 中发送网络请求的话需要如何操作呢?

6.核心概念Actions

6.1Actions的基本使用

Action 类似于 mutation,不同在于:

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

这里有一个非常重要的参数 context:

  • context 是一个和 store 实例均有相同方法和属性的 context 对象;
  • 所以我们可以从其中获取到 commit 方法来提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters;

但是为什么它不是 store 对象呢?这个等到我们讲 Modules 时再具体来说;

代码块:

javascript

mutations: {increment(state) {state.counter++}
},
actions: {increment(context) {context.commit("increment")}
}

6.2Actions的分发操作

如何使用 action 呢?进行 action 的分发:

  • 分发使用的是 store 上的 dispatch 函数;

代码块 1:

javascript

运行

add() {this.$store.dispatch("increment");
}

同样的,它也可以携带我们的参数:

代码块 2:

javascript

运行

add() {this.$store.dispatch("increment", { count: 100 });
}

也可以以对象的形式进行分发:

代码块 3:

javascript

运行

add() {this.$store.dispatch({type: "increment",count: 100,});
}

6.3Actions的mapActions的映射

action 也有对应的辅助函数:

  • 对象类型的写法;
  • 数组类型的写法;

代码块 1(数组类型写法 - 在 methods 中):

javascript

运行

methods: {...mapActions(["increment", "decrement"]),...mapActions({add: "increment",sub: "decrement"})
}

代码块 2(对象类型写法):

javascript

运行

const actions1 = mapActions(["decrement"]);
const actions2 = mapActions({add: "increment",sub: "decrement"
});

6.4用Vuex管理前端异步请求后端数据

7.核心概念Modules

7.1Modules的基本使用

7.2Modules的局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象:

javascript

运行

mutations: {changeName(state) {state.name = "coderwhy"}
},
getters: {info(state, getters, rootState) {return `name:${state.name} age:${state.age} height:${state.height}`}
}

javascript

运行

actions: {changeNameAction({state, commit, rootState}) {commit("changeName", "kobe")}
}

7.3Moudules的命名空间问题

默认情况下,模块内部的 action 和 mutation 仍然是注册在全局的命名空间中的:

  • 这样使得多个模块能够对同一个 action 或 mutation 作出响应;
  • Getter 同样也默认注册在全局命名空间;

如果我们希望模块具有更高的封装度和复用性,可以添加 namespaced: true 的方式使其成为带命名空间的模块:

  • 当模块被注册后,它所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名;

javascript

运行

// 模块定义
const userModule = {namespaced: true,state: () => ({name: "why",age: 18,height: 1.8}),mutations: {changeName(state, payload) {state.name = "coderwhy"}},getters: {info(state, getters, rootState, rootGetters) {return `name:${state.name} age:${state.age} height:${state.height}`}},actions: {changeNameAction({commit, dispatch, state, rootState, getters, rootGetters}) {commit("changeName", "kobe")}}
}
http://www.dtcms.com/a/395415.html

相关文章:

  • 【SSR】SSR 性能问题
  • 《UE教程》第二章第四回——父类蓝图
  • GORM库用法查漏补缺
  • C++11 移动语义与右值
  • FPGA学习笔记——图像处理之对比度调节(直方图均衡化)
  • 如何进行人脸识别
  • 计算机视觉笔试选择题:题组1
  • 第八篇:常量表达式:从const到constexpr的革命
  • RV1126 NO.30:RV1126多线程获取音频AI的PCM数据
  • 基于蚁群算法解决车辆路径问题(VRP)的MATLAB实现
  • C语言自学--C语⾔内存函数
  • 磁带记录仪:从磁带到数字的数据存储之旅
  • 【运维】Ubuntu上WebDAV挂载与自动同步完整指南
  • Ubuntu之旅-04 Docker
  • python(73) 引用.dll文件并调用函数
  • Chrome 学习小记5——demo:(动态壁纸基础)
  • 手写 Android Dex VMP 壳:自定义虚拟机 + 指令解释执行全流程
  • 【Netty】创建一个 SSL 处理器,实现客户端与服务器之间的安全通信
  • 13 Python数据结构与算法
  • 爱:宇宙的心跳
  • Python字节数据写入文本文件完全指南:从基础到高级实战
  • 零基础Windows10安装LLVM
  • selenium三种等待方式详解
  • Leetcode总结速记
  • 手写 Android Dex VMP 壳:指令流 AES 加密 + 动态加载全流程
  • 视频融合平台EasyCVR国标GB28181视频诊断功能详解与实践
  • ORACLE adg 备库也能单独提取AWR报告
  • Angular由一个bug说起之十九:Angular 实现可拓展 Dropdown 组件
  • Kafka核心架构与高效消息处理指南
  • flink1.18配置多个上游source和下游sink