【VueUse】重新定义状态管理在 Vue 中的体验

在 Vue 生态系统中,状态管理一直是开发者们关注的焦点之一。而随着 VueUse 的出现,我们迎来了一种全新的方式来处理状态管理,它让我们能够以更简单、更灵活的方式来管理应用程序的状态。

        在本文中我们将深入探讨 VueUse 中与状态管理相关的内容,从基本的状态响应式到高级的状态管理技巧,以及一些实用的状态管理模式。无论你是刚刚接触 Vue 还是已经是一名经验丰富的 Vue 开发者,本文都将为你带来新的见解和灵感,让你在状态管理的道路上走得更远、更高。

目录

createGlobalState(项目全局状态)

createInjectionState(组件全局状态)

createSharedComposable(封装复用函数)

useAsyncState(异步加载的所有状态)

useLastChanged(获取前后时间差)

useStorage(本地存储数据管理)

useLocalStorage(本地存储空间管理)

useSessionStorage(会话存储空间)

最后总结


createGlobalState(项目全局状态)

将状态保存全局作用域中,以便跨Vue实例复用。什么意思呢?就是实现类似pinia和vuex的功能效果,将数据进行集中状态管理,放在全局上所有的组件都能拿到使用,所有使用这个全局状态的组件都可以共享和修改这个对象。它不需要组件之间的层次结构,可以在任何地方使用。牛犇只能说,如下:

创建一个store文件夹用于存储全局状态数据,借助createGlobalState函数撰写响应式数据,被设置相关的actions和getters函数,并把state和函数return出去。然后在组件中直接使用即可,方法和pinia的写法类似只能说,会pinia使用createGlobalState简直手到擒来!

createGlobalState并没有使用持久化的效果,实现持久化需要再借助另一个API函数,这个后面会讲到,先看看实现的效果吧!非常nice。

createInjectionState(组件全局状态)

创建可以注入到组件中的全局状态。什么意思呢?就是允许在组件之间共享状态,使用类似vue中的 provide / inject API,可以在父组件中创建状态,然后把状态注入到子组件中,子组件来拿到状态数据,这种方式适用于有层次结构的组件之间的状态传递,如下:

我们在仓库中定义一个组件全局状态的函数,我们并不需要给其设置初始值而是通过传参即可:

import { ref, computed } from "vue";
import { createInjectionState } from "@vueuse/shared";const [useProvideCounterStore, useCounterStore] = createInjectionState((initialValue) => {// stateconst count = ref(initialValue)// actions函数const increment = () => {count.value++}// getters函数const double = computed(() => count.value * 2)return { count, double, increment }})// 如果要隐藏“useCounterStore”并将其包装在默认值逻辑中或抛出错误逻辑,请不要导出“useCounterStore”
export { useProvideCounterStore, useCounterStore }

接下来我们通过如下的方式进行父子组件状态的传递,仓库只是一个踏板,真正控制数据的还是父组件,如下:

最终达到的效果如下所示:

createSharedComposable(封装复用函数)

让一个钩子函数可用于多个Vue实例中。什么意思呢?就是用于创建共享的组合式函数,将逻辑组织为可重用的函数,以更好地管理组件的复杂性。意义在于创建一个可以在多个组件之间共享状态和逻辑的组合式函数,避免在多个组件中重复编写相同的逻辑,同时也提高了代码的可维护性和可复用性,如下:

我们随便创建个文件,然后写一个共享的函数,注意createSharedComposable参数只能是一个,如果你想返回多个函数的话需将其放在一个函数里面然后return出去即可,这里我们使用了自带的API函数还有自定义一个函数,然后将这两个公共函数return出去:

import { createSharedComposable, useMouse } from '@vueuse/core'
// 自定义函数
const custom = ()=>{return [ 1, 2 ]
}
// createSharedComposable必须返回一个函数
export const useSharedMouse =   (()=>{return {useMouse,custom}
})

接下来在两个子组件中使用createSharedComposable给我们的共享函数:

然后在父组件中调用这两个子组件即可,发现两个子组件的得到的结果都是一样的,程序复用:

useAsyncState(异步加载的所有状态)

响应式获取异步状态。不会阻塞setup 函数,在promise完成后,将自动触发。什么意思呢?就是为了简化在 Vue 组件中处理异步操作的状态管理。通常情况下,当我们在 Vue 组件中执行异步操作时,需要定义多个状态来表示异步操作的不同阶段(如加载中、成功、失败等),并在组件中进行相应的状态管理和更新。而 useAsyncState 函数可以帮助我们更轻松地管理这些状态。如下:

这里我直接打出 useAsyncState  基本上所有用到的参数,通过注释可以看到每个参数具体的含义

<template><div class="use-async-state">接口数据:{{ ajaxData.state }}<br><br>Ready: {{ ajaxData.isReady ? '准备完毕' : '未准备' }}<br>Loading: {{ ajaxData.isLoading ? '加载中' : '加载完毕' }}</div>
</template><script lang="ts" setup>
import { onMounted, reactive, onBeforeMount } from 'vue'
import { useAsyncState } from '@vueuse/core'const ajaxData = reactive({state: '',isReady: false,isLoading: true,
})const fetchUser = async() => {const resp = await fetch(`https://jsonplaceholder.typicode.com/users/${1}`)return resp.json()
}const { state, isLoading, isReady, error, execute }= useAsyncState(fetchUser, {default: '未加载前的数据'}, {// immediate: false, // 默认是true,控制组件加载后立即执行delay: 2000, // 默认是0,控制组件加载后多久执行onSuccess: () => {ajaxData.state = state.valueajaxData.isLoading = isLoading.valueajaxData.isReady = isReady.valueconsole.log('useAsyncState', state.value);},onError: () => {}, // 错误处理
})console.log('setup函数中',state.value);
onMounted(()=>{console.log('我是挂载后',state.value);
})
onBeforeMount(()=>{console.log('我是挂载前',state.value);
})
</script>

我设置了延迟两秒执行获取接口数据,结果如下:

代码中的immediate属性,搭配execute可以手动控制函数的执行,如下:

<template><div class="use-async-state">接口数据:{{ ajaxData.state }}<br><br>Ready: {{ ajaxData.isReady ? '准备完毕' : '未准备' }}<br>Loading: {{ ajaxData.isLoading ? '加载中' : '加载完毕' }}<br /><button @click="loadData">重新加载数据</button></div>
</template><script lang="ts" setup>
import { onMounted, reactive, onBeforeMount } from 'vue'
import { useAsyncState } from '@vueuse/core'const ajaxData = reactive({state: '',isReady: false,isLoading: true,
})const fetchUser = async(id: number) => {const resp = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)return resp.json()
}const { state, isLoading, isReady, error, execute }= useAsyncState(fetchUser, {default: '未加载前的数据'}, {immediate: false, // 默认是true,控制组件加载后立即执行delay: 2000, // 默认是0,控制组件加载后多久执行onSuccess: () => {ajaxData.state = state.valueajaxData.isLoading = isLoading.valueajaxData.isReady = isReady.valueconsole.log('useAsyncState', state.value);},onError: () => {}, // 错误处理
})let id = 0const loadData = () => {ajaxData.state = ''ajaxData.isReady = falseajaxData.isLoading = trueid+=1execute(2000, id) // 执行函数,第一个参数是延迟时间,第二个参数是函数的参数
}console.log('setup函数中',state.value);
onMounted(()=>{loadData()console.log('我是挂载后',state.value);
})
onBeforeMount(()=>{console.log('我是挂载前',state.value);
})
</script>

最终呈现的效果如下,效果很不错!

useLastChanged(获取前后时间差)

记录最后一次更改的时间戳。什么意思呢?就是跟踪指定的值在最后一次发生改变的时间,这在某些场景下非常有用,比如监控某个状态的变化时间,或者执行一些特定的操作,当值发生改变时,为开发者提供方便的状态管理功能,从而提高开发效率和代码可维护性。如下:

这里我通过useLastChanged获取输入框前后输入数据的时间差:

<template><div class="use-last-changed"><input v-model="input" /> <br />lastChanged: {{ lastChanged }}<br />formattedTime: {{ formattedTime }}</div>
</template><script setup lang="ts">
import { ref, watch } from 'vue'
import { useLastChanged } from '@vueuse/core'const input = ref('')
const lastChanged = useLastChanged(input)
// 格式化时间的 ref
const formattedTime = ref('')watch(lastChanged, () => {const timestamp = lastChanged.value?.toString()const date = new Date(Number(timestamp))const year = date.getFullYear()const month = date.getMonth() + 1const day = date.getDate()const hours = date.getHours()const minutes = date.getMinutes()const seconds = date.getSeconds()// 更新 formattedTimeformattedTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
})
</script>

达到的效果如下,总体来说还是不错的api:

useStorage(本地存储数据管理)

响应式 LocalStorage/SessionStorage。什么意思呢?就是它用于在 Vue 组件中管理浏览器本地存储(LocalStorage 或 SessionStorage)的值。通过使用 useStorage,你可以方便地在 Vue 组件中读取、写入和监听本地存储中的数据,而不需要手动处理存储和监听逻辑。这可以帮助你更轻松地在应用程序中实现持久化数据的存储和状态管理。

如下我们对useStorage进行一个最简便的操作演示,默认情况下,useStorage将使用存储中的值,如果它存在就会忽略你传入的默认值。忽略的默认值去访问肯定是未定义的。

最终得到如下的结果,父组件存储的数据是持久化的,修改或删除本地数据,子组件也会发生变化,注意useStorage默认是存储本地存储空间,即localStorage

如果你想采用sessionStorage存储的话,给useStorage传递第三个参数即可,如下:

结果如下,这里不再赘述:

合并默认值:如果你想对原本本地存储的数据和你传递的默认值进行合并的话,可以采用如下的操作,启用mergeDefaault选项,将其设置为true时,它将对对象执行浅合并:

您可以传递一个函数来执行自定义合并(例如深度合并),例如:

const state = useStorage('my-store',{ hello: 'hi', greeting: 'hello' },localStorage,{ mergeDefaults: (storageValue, defaults) => deepMerge(defaults, storageValue) }
)

自定义序列化

默认情况下,useStorage会根据提供的默认值的数据类型巧妙地使用相应的序列化程序。例如,JSON. stringify/JSON.parse将用于对象,Number.toString/parseFloat用于数字等,还可以提供自己的序列化函数来使用Storage,如下:

import { useStorage } from '@vueuse/core';// 自定义序列化
const store = useStorage('key','123',undefined,{serializer: {read: (v: any) => v ? JSON.parse(v) : null,write: (v: any) => JSON.parse(v),},},
)
console.log(store.value) // 123
store.value = null
console.log(store.value) // null

请注意,当您提供null作为默认值时,useStorage不能从中假定数据类型。在这种情况下,您可以提供自定义序列化程序或显式重用内置序列化程序。

import { StorageSerializers, useStorage } from '@vueuse/core'const objectLike = useStorage('key', null, undefined, { serializer: StorageSerializers.object })
objectLike.value = { foo: 'bar' }

useLocalStorage(本地存储空间管理)

响应式的 LocalStorage。什么意思呢?就是用于在 Vue 组件中使用本地存储的 Hook。它能够方便地将数据保存在浏览器的本地存储中,并且自动将存储的数据与 Vue 组件中的状态进行同步。

和上面useStorage相比就是这个更具针对性,用法一样:

useSessionStorage(会话存储空间)

响应式SessionStorage。什么意思呢?就是用于在 Vue 组件中使用会话存储的 Hook。与 useLocalStorage 类似,它能够方便地将数据保存在浏览器的会话存储中,并且自动将存储的数据与 Vue 组件中的状态进行同步。

最后总结

createGlobalState:用于在任何组件中共享状态,创建一个全局响应式对象。

createInjectionState:用于在父子组件之间传递状态,通过provide/inject API实现。

createSharedComposable:封装复用函数,多组件共享函数的状态和逻辑。

useAsyncState:简化在 Vue 组件中处理异步操作的状态管理,减少重复的状态管理代码。

useLastChanged:跟踪指定的值在最后一次发生改变的时间。

useStorage:用于在Vue组件中管理浏览器本地存储LocalStorage或SessionStorage的值。

useLocalStorage:利用浏览器的本地存储功能,实现数据的持久化存储和状态管理。

useSessionStorage:将数据保存在会话存储中,实现临时性数据的存储和状态管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/2978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

StrongSORT——基于DeepSORT,提高多目标跟踪的准确性和鲁棒性

1、概述 1.1 DeepSORT DeepSORT算法是在SORT基础上发展起来的一种多目标跟踪算法。SORT算法结合了目标检测器和跟踪器&#xff0c;其中跟踪器的核心是卡尔曼滤波和匈牙利算法。 卡尔曼滤波用于预测目标在下一帧的位置和状态而匈牙利算法则用于将预测状态与实际检测结果进行最…

SpringCloud基础 Consul的引入

前言 首先是为什么引入consul这个组件 我们知道微服务分为很多个模块,这里模块中相互调用,我使用硬编码的模式是不好的 比如微服务模块需要更新的时候,我们使用硬编码的方式可能需要修改很多个地方 但是使用consul之后,就引入了注册中心,我们只需要将对应的服务注册为节点 这样…

android脱壳第二发:grpc-dumpdex加修复

上一篇我写的dex脱壳&#xff0c;写到银行类型的app的dex修复问题&#xff0c;因为dex中被抽取出来的函数的code_item_off 的偏移所在的内存&#xff0c;不在dex文件范围内&#xff0c;所以需要进行一定的修复&#xff0c;然后就停止了。本来不打算接着搞得&#xff0c;但是写了…

【论文阅读】EgoPCA: A New Framework for Egocentric Hand-Object Interaction

论文主要贡献 提出一种新的框架&#xff1a;Ego-HOI recognition by Probing, Curation and Adaption (EgoPCA)。构建了全面的预训练集&#xff0c;平衡的测试集&#xff0c;以及一个包含了微调策略的baseline。 在Ego-HOI达到了SOTA&#xff0c;并且建立了有效的机制方法。 …

【后端】git与python的结合使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、git介绍二、git常见使用三、git与python的结合使用四、总结 前言 随着开发语言及人工智能工具的普及&#xff0c;使得越来越多的人会主动学习使用一些开发…

【OceanBase系列】—— 常用运维操作(备忘)

作者简介&#xff1a; 花名&#xff1a;绪宁&#xff0c;OceanBase 数据库解决方案架构师 创建租户 方法一&#xff1a;OCP 创建 确认可分配资源 具体可以分配多少内存&#xff0c;可以通过【资源管理】查看各节点的剩余资源 2. 新建租户 3. 填写租户信息 zone 优先级主要是 p…

STM32玩转物联网实战篇:5.ESP8266 WIFI模块MQTT通信示例详解

1、准备开发板 开发板功能区分布图 开发板俯视图 2、实验讲解 在之前的章节中&#xff0c;已经讲解过了MQTT的通讯原理和组包过程&#xff0c;现在开始手把手的教大家用代码来实现连接MQTT平台以及数据的交互&#xff0c;实际上这篇文章已经拖更接近两年了&#xff0c;非常…

《QT实用小工具·三十九》仿 Windows10 画图3D 的颜色选择器, 但更加强大

1、概述 源码放在文章末尾 该项目实现了仿 Windows10 画图3D 的颜色选择器&#xff0c;功能更加丰富更加强大。 项目部分代码如下所示&#xff1a; import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtGraphicalEffects 1.15Item {id…

【leetcode面试经典150题】72. 从前序与中序遍历序列构造二叉树(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

opencv绘制线段------c++

绘制线段 bool opencvTool::drawLines(std::string image_p, std::vector<cv::Point> points) {cv::Mat ima cv::imread(image_p.c_str()); // 读取图像&#xff0c;替换为你的图片路径 cv::Scalar red cv::Scalar(0, 0, 255); // Red color int thickness 2;// 遍…

面试遇到算法题:实现LRU缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。 这是一道大厂面试高频出现的算法题&#xff0c;难度为⭐️⭐️⭐️&#xff0c;属于中等&#xff0c;老铁们来一起看看这个题该怎么解&#xff1f; 1. 原题再现 没有废话&#xff0c;翠花&#xff0c;上酸菜&…

JS 添加数组元素( 4种方法 )

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、四种…

Spring Boot 集成 EasyExcel 3.x

Spring Boot 集成 EasyExcel 3.x Spring Boot 集成 EasyExcel 3.x 本章节将介绍 Spring Boot 集成 EasyExcel&#xff08;优雅实现Excel导入导出&#xff09;。 &#x1f916; Spring Boot 2.x 实践案例&#xff08;代码仓库&#xff09; 介绍 EasyExcel 是一个基于 Java 的、…

HZNUCTF -- web

HZNUCTF第五届校赛实践赛初赛 Web方向 WriteUp-CSDN博客 ezssti 下载文件 访问 /login 可由源代码中看到 Eval 函数 &#xff0c;可以任意命令执行 按照格式&#xff0c;可执行命令 POST &#xff1a;name{{.Eval "env"}} 可以得到flag &#xff08;尝试ls 只能列出…

「ChatGPT」掀起新一轮AI热潮!超越GPT-4 Turbo,商汤日日新大升级!

目录 拳打 GPT-4 Turbo &#xff0c;脚踢 DALLE 3 端侧大模型&#xff0c;唯快不破 AI 应用落地需要一个即插即用的大模型超市 并不存在 AI 这个行业&#xff0c;只有 AI行业&#xff0c;强调 AI 需要与传统产业合作&#xff0c;这种关系是结合与赋能&#xff0c;而不是颠覆…

java开发之路——用户管理中心_简单初始化

用户管理中心_简单初始化 (一) 初始化项目1. 使用 Ant Design Pro(现成的管理系统) 进行前端初始化2. 后端初始化三种初始化java项目 (二) 遇到的问题【问题1】Ant design pro页面打不开&#xff0c;一直在budiling控制台出现错误error-./src/components/index.ts【问题2】初始…

基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…

如何通过cURL库实现远程控制插座

如何通过cURL库实现远程控制插座呢&#xff1f; 本文描述了使用cURL库调用HTTP接口&#xff0c;实现控制插座&#xff0c;即插即用&#xff0c;先插入插座&#xff0c;再接电器&#xff0c;实现远程控制。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格…

udp/tcp错误总结

udp tcp——多进程 tcp——多线程 tcp——线程池 tcp——守护进程 &#x1f386;udp  ✨pthread_create 错误总结  ✨LockGuard错误总结  ✨服务端需要写成多线程  ✨客户端也需要写成多线程  ✨多线程调试工具 &#x1f386;tcp  ✨tcp独有调试工具——telnet  ✨Threa…

基于瞬时频率的语言信号清/浊音判决和高音检测(MATLAB R2021)

语音是由气流激励声道从嘴唇或鼻孔辐射出来而产生的。根据声带是否振动&#xff0c;发音可分为浊音和清音。浊音和清音有明显的区别&#xff0c;浊音具有周期信号的特征&#xff0c;而清音则具有随机噪声的特征&#xff1b;浊音在频域上具有共振峰结构&#xff0c;其能量主要集…