Vue3 中Ref的最佳实践

alt

在vue3中如果我们需要获取一个响应式的变量,可以使用ref来定义一个变量。

const name = ref( "" );
name.value = "test"

定义好后,就可以实现修改状态,更新UI的效果了。

在这个基础上,本文主要讨论跨组件时如何管理Ref的状态,以及如何更好地封装Ref的读写。

单向数据流

https://cn.vuejs.org/guide/components/props#one-way-data-flow

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

举一个例子,我们要实现一个header里面有一个menu的按钮,当点击了会把侧边栏滑进来,然后点击了关闭按钮又隐藏。

<script>
import { ref } from "vue";
import Header from "./components/Header.vue";
import Nav from "./components/Nav.vue";
export default {
  components: {
    Header,
    Nav,
  },
  setup() {
    const isOpen = ref(false);
    const handToggle = () => isOpen.value = !isOpen.value;
    return { isOpen, handToggle };
  },
};
</script>

<template>
  <Header @toggle="handToggle" />
  <Nav :isOpen="isOpen" @toggle="handToggle" />
</template>

header.vue

<script>
export default {
  setup(props, { emit }) {
    const handleMenu = () => {
      emit("toggle");
    };
    return { handleMenu };
  },
};
</script>

<template>
  <header>
    <nav>
      <button @click="handleMenu">menu</button>
    </nav>
  </header>
</template>

nav.vue

<script>
export default {
  props: {
    isOpen: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, { emit }) {
    const handleMenu = () => {
      emit("toggle");
    };
    return { props, handleMenu };
  },
};
</script>

<template>
  <div :class="['nav', { open: props.isOpen }]">
    <a @click="handleMenu">close</a>
  </div>
</template>

整体的效果如下所示:

alt

在点击了Header.vue里面的Menu 后发送一个emit给上层的component,透过上层接收到的toggle事件去修改我们isOpen的value,然后isOpen再透过props 传入Nav.vue里面去控制侧边选单的开起,然后透过侧边选单的close按钮,在发送一个 toggle事件往上去更改 isOpen,然后props 的isOpen也会同步知道被更改,这样就完成了整个的流程!

整个流程其实也遵循了单一数据流的特性。props都是从父级修改的。但是不是感觉把简单的事情复杂化了?增加了一层事件的往返通信来修改props。有没有更方便的做法?其实我们可以直接去掉事件这一层。

<script>
// 其他省略...
export default {
  setup() {
    const isOpen = ref(false);
    const handleOpenMenu = () => isOpen.value = !isOpen.value;
    return { isOpen, handleOpenMenu };
  },
};
</script>

<template>
  <Header :handleOpenMenu="handleOpenMenu" />
  <Nav :isOpen="isOpen" :handleOpenMenu="handleOpenMenu" />
</template>

把修改值的方法传递给子组件去调用,避免了事件的通信。调整后的数据流就简单多了。

alt

单向数据流是一种数据流动的模式,数据从父组件流向子组件,子组件只能通过props从父组件接收数据,而不能直接修改父组件的状态。这种模式使得数据流更加清晰和可预测,有助于维护和管理大型应用。

在react中也有类似的效果"Lifting State Up"(状态提升),当多个组件需要共享同一个状态时,可以将状态提升到它们共同的父组件中。这样,子组件可以通过props从父组件获取状态,并通过回调函数通知父组件更新状态。状态提升有助于避免组件间的直接状态共享,使得状态管理更加集中和一致。

封装

你可能会疑惑,上面子组件调用handleOpenMenu不是直接修改props么?这种不属于直接修改,控制权是在父组件。我们把修改的方式用函数封装起来了,隐藏了中间的实现细节。这就是一种封装。最典型的例子就是react的useState。

import { useState } from  'react' ;

function  MyComponent () {
   const [age, setAge] = useState ( 28 );
   const [name, setName] = useState ( 'Taylor' );
   const [address, setAddress] = useState ( 'Taiwan' );
}

在React中,useState允许开发者创建一个状态变量和一个设置这个状态的函数。这样,开发者可以通过这个函数来修改状态,而不是直接操作状态变量。这种方式简化了状态管理,并且使得状态的修改更加可控。

虽然在小型项目中直接使用.value来修改Vue中的响应式数据非常方便,但在需要传递props或者使用emit进行父子组件通信时,还是需要定义设置函数。随着项目的增大或者时间的推移,可能会出现同时使用.value和设置函数的情况,这可能会让代码变得混乱。所以最好是我们统一封装规范它的使用,我们封装一个useRefState来管理状态。

import { shallowRef } from  "vue" ;

export  function  useRefState ( baseState ) {
   const state = shallowRef (baseState);
   const  update = ( newValue ) => {
    state. value = newValue;
  };
  return [state, update];
}

这么一来我们就可以在开发的时候使用跟React 一样的方式来定义状态。

< script  setup > import { useState } from "./composables/useRefState.js" ;
   const [name, setName] = useRefState ( "mike" );
   const [info, setInfo] = useRefState ({
     name : "mike" ,
     age : 12 ,
   
  });
</ script >

< template > 
  < h2 > name: {{ name }} </ h2 > 
  < pre > info: {{ info }} </ pre >

  < input  type = "text"  v-model = "name" />
  
  < button @ click = "setName('jacky')" > set name </ button > 
  < button @ click = "setInfo({ name: 'andy', age: 20 })" > set info </ button > 
</ template >

它的好处是可以减少每次在定义ref的时候都要再写一个set function ,造成code 会很多很杂的问题,而且也不会失去响应式的特性!

在上面的实现中,我们使用的是shallowRef而非ref。

这段内容主要解释了在Vue框架中,refshallowRef的区别以及shallowRef的用途。ref是Vue中的一个响应式API,它将内部值转换为响应式对象。与ref不同,shallowRef内部的值不会自动转换为响应式。只有当你通过.value属性设置值时,这个操作才是响应式的。也就是说,只有第一层的属性是响应式的。

shallowRef常用于大型数据结构的性能优化和与外部状态管理系统的整合。由于shallowRef只在第一层属性上是响应式的,这使得它在处理大型或深层数据结构时,性能更好。因为不需要对每个深层属性都进行响应式处理,减少了性能开销。

当我们使用useRefState之后,就不能直接针对单个属性去set value,需要将原本的物件加上新的值一起写入。

const [info, setInfo] = useState ({
     name : "mike" ,
     age : 12 ,
});

// 把所有属性一起写入
setInfo ({
  ...info,
  address : addr,
});

基本上就跟我直接替换整个object 一样,所以这边就不需要使用ref来增加无谓的效能消耗。

总结

通过本文的探讨,我们可以看到Vue 3的响应式系统为我们提供了强大的工具来管理跨组件的状态。无论是通过事件通信还是直接传递方法来更新状态,Vue都提供了灵活的解决方案。封装useRefState的做法,让我们能够以一种更加接近React的useState的方式来处理Vue中的状态,使得状态管理更加直观和一致。

最后,无论我们选择哪种方式来管理状态,重要的是要确保我们的应用遵循清晰的数据流和一致的设计模式。这样,随着项目的增长和复杂性的增加,我们仍然能够保持代码的可维护性和可扩展性。

本文由 mdnice 多平台发布

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

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

相关文章

Discord:报错:A fatal Javascript error occured(解决办法)

按 Windows 键 R 并输入 %appdata% 选择 discord 文件夹并将其删除。 再次按 Windows 键 R 并输入 %LocalAppData% 选择 discord 文件夹并再次将其删除。 附加&#xff1a; 如果还不行&#xff0c;就通过官网下载吧&#xff0c;这个问题通过epic下载可能会有

Python并发编程挑战与解决方案

Python并发编程挑战与解决方案 并发编程是现代软件开发中的一项核心能力&#xff0c;它允许多个任务同时运行&#xff0c;提高程序的性能和响应速度。Python因其易用性和灵活性而广受欢迎&#xff0c;但其全局解释器锁&#xff08;GIL&#xff09;以及其他特性给并发编程带来了…

Docker面试-24年

1、Docker 是什么&#xff1f; Docker一个开源的应用容器引擎&#xff0c;是实现容器技术的一种工具&#xff0c;让开发者可以打包他们的应用以及环境到一个镜像中&#xff0c;可以快速的发布到任何流行的操作系统上。 2、Docker的三大核心是什么? 镜像&#xff1a;Docker的…

网络威胁情报技术的进步

网络威胁形势不断演变&#xff0c;必然导致防御者和攻击者之间持续展开军备竞赛。幸运的是&#xff0c;网络威胁情报 (CTI) 技术的进步为安全专业人员提供了强大的工具&#xff0c;使他们能够保持领先地位。 本指南深入探讨了 CTI 的最新进展&#xff0c;让您了解这些技术如何…

【学习笔记】手写一个简单的 Spring MVC

目录 一、什么是Spring MVC &#xff1f; Spring 和 Spring MVC 的区别&#xff1f; Spring MVC 的运行流程&#xff1f; 二、实现步骤 1. DispatcherServlet 1. 创建一个中央分发器 拦截所有请求 测试 2. 接管 IOC 容器 1. 创建配置文件 2. 修改 web.xml 配置文件 …

1分钟搞懂K8S中的NodeSelector

文章目录 NodeSelector是什么&#xff1f;为什么使用NodeSelector&#xff1f;怎么用NodeSelector&#xff1f;POD配置示例yaml配置示例 如何知道K8S上面有哪些节点&#xff0c;每个节点都有什么信息呢&#xff1f;1. 使用kubectl命令行工具查看所有节点及其标签2. 使用kubectl…

算法【Java】—— 二叉树的深搜

深搜 深搜简单来说就是一直递归到底&#xff0c;然后返回&#xff0c;以二叉树为例&#xff0c;就是从根节点出发一直搜索到叶子节点&#xff0c;然后想上返回。 这里简单说明一下&#xff1a;深搜的英文缩写是 dfs&#xff0c;下面定义深搜函数名我直接命名为 dfs 实战演练 …

内存占用估算方法

优质博文&#xff1a;IT-BLOG-CN 通过掌握每种数据类型的大小&#xff0c;就可以更准确地预测对象和数据的内存消耗。 一、基础数据类型 Java基础数据类型结构&#xff0c;在64位系统开启指针压缩情况下的内存占用字节数&#xff1a; booleanbytecharshortintlongfloatdoub…

PYTHON实现HTTP request的一些有用的函数

前言 我们知道&#xff0c;当需要设计一个程序和服务器进行交互时&#xff0c;往往会用到HTTP的request&#xff0c;即服务器有一个对外接口REST API&#xff0c;因此当向服务器发送符合格式要求的HTTP request时&#xff0c;服务器会给出响应&#xff0c;甚至执行一些任务。如…

码随想录算法训练营第62天|卡码网:97. 小明逛公园、127. 骑士的攻击

1. 卡码网 97. 小明逛公园 题目链接&#xff1a;https://kamacoder.com/problempage.php?pid1155 文章链接&#xff1a;https://www.programmercarl.com/kamacoder/0097.小明逛公园.html 思路&#xff1a; 使用Floyd 算法&#xff0c;目的是解决多源最短路问题&#xff0c;即 …

如何编写一个优雅的commit message

在Git中&#xff0c;git commit 命令扮演着至关重要的角色。它的主要作用是将暂存区&#xff08;staging area&#xff09;里的改动内容提交到本地仓库&#xff08;repository&#xff09;中&#xff0c;形成一个新的版本或提交&#xff08;commit&#xff09;。这个过程是 Git…

基于Node2Vec的图嵌入实现过程

目录 一、引言二、Node2Vec&#xff08;原理&#xff09;2.1 随机游走&#xff08;Random Walk&#xff09;2.2 嵌入学习2.3 Node2Vec 的优势 三、使用 Node2Vec 进行图嵌入&#xff08;实践&#xff09;3.1 读取和转换 JSON 文件为 Graph 对象3.2 训练 Node2Vec 模型3.3 二维嵌…

10款好用的开源 HarmonyOS 工具库

大家好&#xff0c;我是 V 哥&#xff0c;今天给大家分享10款好用的 HarmonyOS的工具库&#xff0c;在开发鸿蒙应用时可以用下&#xff0c;好用的工具可以简化代码&#xff0c;让你写出优雅的应用来。废话不多说&#xff0c;马上开整。 1. efTool efTool是一个功能丰富且易用…

Kotlin:2.0.20 的新特性

一、概述 Kotlin 2.0.20英文版官方文档 Kotlin 2.0.20发布了!这个版本包括对Kotlin 2.0.0的性能改进和bug修复&#xff0c;我们在其中宣布Kotlin K2编译器为Stable。以下是本次发布的一些亮点: 数据类复制函数将具有与构造函数相同的可见性来自默认目标层次结构的源集的静态访…

Python批量下载PPT模块并实现自动解压

日常工作中&#xff0c;我们总是找不到合适的PPT模板而烦恼。即使有免费的网站可以下载&#xff0c;但是一个一个地去下载&#xff0c;然后再批量解压进行查看也非常的麻烦&#xff0c;有没有更好方法呢&#xff1f; 今天&#xff0c;我们利用Python来爬取一个网站上的PPT&…

HTML+CSS基础用法介绍五

目录&#xff1a; 结构伪类选择器盒子模型-边框线盒子模型-内边距盒子模型-解决盒子被撑大盒子模型-外边距与版心居中小知识&#xff1a;清除浏览器中所有标签的默认样式内容溢出控制显示方式盒子模型-圆角 &#x1f40e;正片开始 结构伪类选择器 什么是结构伪类选择器&…

全新一区PID搜索算法+TCN-LSTM+注意力机制!PSA-TCN-LSTM-Attention多变量时间序列预测(Matlab)

全新一区PID搜索算法TCN-LSTM注意力机制&#xff01;PSA-TCN-LSTM-Attention多变量时间序列预测&#xff08;Matlab&#xff09; 目录 全新一区PID搜索算法TCN-LSTM注意力机制&#xff01;PSA-TCN-LSTM-Attention多变量时间序列预测&#xff08;Matlab&#xff09;效果一览基本…

66 使用注意力机制的seq2seq_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录动机加入注意力总结代码定义注意力解码器训练小结练习 我们来真的看一下实际应用中&#xff0c;key&#xff0c;value&#xff0c;query是什么东西&#xff0c;但是取决于应用场景不同&#xff0c;这三个东西会产生变化。先将放在seq2seq这个…

Linux dlsym符号查找疑惑分析

dlsym 函数是 Linux 下动态链接库&#xff08;shared library&#xff09;编程中的一个重要函数。它用于在运行时获取动态链接库中符号的地址&#xff0c;通常用于获取函数指针或变量的地址。 以下是 dlsym 函数的基本用法和示例。 1. 函数原型 void *dlsym(void *handle, c…

如何实现事件流操作

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了通道相关的内容,本章回中将介绍StreamProvider组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 在Flutter中Stream是经常使用的组件,对该组件的监听可void main() {///让状态栏和程序的appBar融为一体…