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

2024-04-25 06:36

本文主要是介绍【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:将数据保存在会话存储中,实现临时性数据的存储和状态管理。

这篇关于【VueUse】重新定义状态管理在 Vue 中的体验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/933932

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

css渐变色背景|<gradient示例详解

《css渐变色背景|<gradient示例详解》CSS渐变是一种从一种颜色平滑过渡到另一种颜色的效果,可以作为元素的背景,它包括线性渐变、径向渐变和锥形渐变,本文介绍css渐变色背景|<gradien... 使用渐变色作为背景可以直接将渐China编程变色用作元素的背景,可以看做是一种特殊的背景图片。(是作为背

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...