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

news2024/12/23 20:48:53

在 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) => {
    // state
    const 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.value
    ajaxData.isLoading = isLoading.value
    ajaxData.isReady = isReady.value
    console.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.value
    ajaxData.isLoading = isLoading.value
    ajaxData.isReady = isReady.value
    console.log('useAsyncState', state.value);
  },
  onError: () => {}, // 错误处理
})

let id = 0

const loadData = () => {
  ajaxData.state = ''
  ajaxData.isReady = false
  ajaxData.isLoading = true
  id+=1
  execute(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() + 1
  const day = date.getDate()
  const hours = date.getHours()
  const minutes = date.getMinutes()
  const seconds = date.getSeconds()
  // 更新 formattedTime
  formattedTime.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.coloradmin.cn/o/1621710.html

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

相关文章

成功解决ImportError: cannot import name ‘builder‘ from ‘google.protobuf.internal

成功解决ImportError: cannot import name builder from google.protobuf.internal 目录 解决问题 解决思路 解决方法 解决问题 ImportError: cannot import name builder from google.protobuf.internal 解决思路 导入错误:无法从“google.protobuf.internal”导入名称“…

Vscode配置C/C++编程环境@配置C和CPP的运行和调试环境@配置过程的相关问题@中文文件名乱码@build和debug方案组合配置

文章目录 abstractgcc/g文档和用法常见用例 目录.vscode中的相关文件说明tasks.jsonlaunch.jsonc_cpp_properties.json IDE或编辑器配置vscode配置相关指令和快捷键默认task配置和取消默认 配置文件C/C共用一组tasks.json/launch.json文件?关于注释内容示例&#x1f47a;tasks…

【linux】进程地址被占用

在强制关闭一个udp程序后&#xff0c;重启该程序报错&#xff1a; bind error: Address already in use 查找并关闭占用端口的进程&#xff1a; 首先&#xff0c;确定哪个进程占用了目标端口。在Linux系统中&#xff0c;可以使用以下命令&#xff1a; netstat -tulnp | grep …

TensorFlow进阶二(高阶操作)

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

Flutter 上架如何解决 ITMS-91053 问题

最近&#xff0c;我的 Flutter App 发布到 TestFlight 后&#xff0c;就会收到一封邮件&#xff1a;The uploaded build for YOUR APP has one or more issues. 上面的邮件主要是说&#xff0c;我的 App 缺少了调用 API 的声明&#xff0c;以前从来没看到过&#xff0c;上网一查…

Python构建学生信息管理系统:构建RESTful API - 学生信息管理系统的后端逻辑

在之前的博客里&#xff0c;我们已经完成了项目初始化&#xff0c;在本篇博客中&#xff0c;我们将深入探讨如何使用Flask框架实现学生信息管理系统的后端逻辑&#xff0c;特别是通过RESTful API来实现学生信息的增删改查&#xff08;CRUD&#xff09;操作。 Flask RESTful AP…

传染病模型SIR及其变体(python版本)

文章目录 传染病模型及其变体1. SI模型1.1代码2. SIS模型2.1 代码3. 基本再生数 basic reproductive number4. SIR模型4.1 代码5. SEIR模型5.1 代码6. SEIJR模型6.1 代码7. SEIJRD模型7.1 代码传染病模型及其变体 1. SI模型 在该模型里面,群体中只有两种人:易感者和感染者。…

数据可视化-ECharts Html项目实战(14)

在之前的文章中&#xff0c;我们深入学习ECharts鼠标左键触发。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数据可视化-ECharts Html项目实战&#xff08;…

Open-Sora:开源版的Sora

项目简介 本项目希望通过开源社区的力量复现Sora&#xff0c;由北大-兔展AIGC联合实验室共同发起&#xff0c;当前我们资源有限仅搭建了基础架构&#xff0c;无法进行完整训练&#xff0c;希望通过开源社区逐步增加模块并筹集资源进行训练&#xff0c;当前版本离目标差距巨大&…

java多功能手机

随着科技的发展&#xff0c;手机的使用已经普及到每个家庭甚至个人&#xff0c;手机的属性越来越强大&#xff0c;功能也越来越多&#xff0c;因此人们在生活中越来越依赖于手机。 任务要求&#xff0c;使用所学知识编写一个手机属性及功能分析程序设计&#xff0c;测试各个手机…

国产POL8903 LVDS转MIPI带旋转功能方案介绍

一、芯片简介 1、系统 高性能 MIPS 32bit CPU 内核&#xff1a; 高性能 DSP 内核图像处理单元 16KB指令 Cache 16KB数据 Cache 96KB片上 SRAM 内嵌DDR3控制器 2、 LVDS 输入 支持1或者2通道 LVDS 输入 支持最大1920x108060Hz输入 兼容 VESA 和 JEIDA 格式 通道内5条…

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

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

设计模式-状态模式在Java中的使用示例-信用卡业务系统

场景 在软件系统中&#xff0c;有些对象也像水一样具有多种状态&#xff0c;这些状态在某些情况下能够相互转换&#xff0c;而且对象在不同的状态下也将具有不同的行为。 为了更好地对这些具有多种状态的对象进行设计&#xff0c;我们可以使用一种被称之为状态模式的设计模式…

Adobe Illustrator 2024 v28.4.1 (macOS, Windows) - 矢量绘图

Adobe Illustrator 2024 v28.4.1 (macOS, Windows) - 矢量绘图 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、Lightroom Classic、Media Encoder、Photoshop、Premiere Pro、Adobe XD 请…

【注解和反射】获取类运行时结构

继上一篇博客【注解和反射】类加载器-CSDN博客 目录 七、获取类运行时结构 测试 getFields()和getDeclaredFields() getMethods()和getDeclaredMethods() 七、获取类运行时结构 获取类运行时结构通常指的是在Java等面向对象编程语言中&#xff0c;使用反射&#xff08;Ref…

【git学习】Git 的基本操作

文章目录 &#x1f680;创建 Git 本地仓库&#x1f680;配置 Git&#x1f680;认识⼯作区、暂存区、版本库&#x1f680;添加⽂件操作 &#x1f680;创建 Git 本地仓库 仓库是进⾏版本控制的⼀个⽂件⽬录。我们要想对⽂件进⾏版本控制&#xff0c;就必须先创建⼀个仓库出来。 …

谷歌发布基于声学建模的无限虚拟房间增强现实鲁棒语音识别技术

声学室模拟允许在AR眼镜上以最少的真实数据进行训练&#xff0c;用于开发鲁棒的语音识别声音分离模型。 随着增强现实&#xff08;AR&#xff09;技术的强大和广泛应用&#xff0c;它能应用到各种日常情境中。我们对AR技术的潜能感到兴奋&#xff0c;并持续不断地开发和测试新…

web前端框架设计第六课-样式绑定

web前端框架设计第六课-样式绑定 一.预习笔记 1.class属性绑定 给P标签绑定一个类样式&#xff0c;类名为active。当active取值为true时&#xff0c;表示绑定样式成功&#xff0c;取值为false时&#xff0c;取消绑定 以对象形式给P标签绑定多个类样式 以数组形式给P标签绑定多…

网络安全新挑战:通用人工智能(AGI)等级保护指南

通用人工智能&#xff08;AGI&#xff09;的发展现状及趋势 随着2023年大语言模型应用的划时代突破&#xff0c;以ChatGPT为杰出代表的此类技术犹如一股洪流&#xff0c;彻底颠覆了人类与机器智能交互的疆界&#xff0c;引领通用人工智能&#xff08;AGI&#xff09;步入一个崭…

网络基础(day3)建议在电脑端注册登陆观看!!!

【 理论重点】 网络是什么&#xff1f; &#xff08;网络是载体&#xff0c;目的是传输互联网中的数据&#xff0c;数据是终端产生<手机、电脑、服务器等>。&#xff09; 如何组件网络&#xff08;良性网络架构&#xff09;&#xff1f;有网络架构思维&#xff0c;得按层…