分析 Vue 3 页面数据加载延迟问题

1. 问题描述

在 Vue 3 的项目中,当我们使用响应式数据(如 ref 或 computed)来管理页面状态时,可能会遇到由于接口数据加载延迟,导致页面初始渲染时数据尚未获取完成的问题。这会导致页面在数据未更新的情况下提前渲染出来,从而产生显示错误或不完整的内容。

针对此问题简单分析下原因和解决方法。

1、Vue 响应式系统的工作机制

Vue 3 的响应式系统基于 Proxy 实现,通过 reactive、ref 或 computed 等 API 来响应数据的变化。当数据变化时,Vue 会自动重新渲染与这些数据绑定的 DOM 元素。但是,如果数据在初始渲染时为空或未加载,Vue 会将页面渲染为初始状态,直到数据加载完毕并触发响应式更新。

🌰

假设存在一个 ref 类型的响应式数据 data,在组件加载时该数据为空,且需要从接口获取数据。

import { defineComponent, ref, onMounted } from 'vue';

export default defineComponent(() => {
  const data = ref(''); // 初始值为空

  onMounted(async () => {
    // 模拟延迟获取接口数据
    await new Promise((resolve) => setTimeout(resolve, 3000));
    data.value = '已加载的数据'; // 设置接口返回的数据
  });

  return () => (
    <div>
      <p>{data.value ? data.value : '加载中...'}</p>
    </div>
  );
});

效果:组件渲染时,data.value 为 null,所以会展示 加载中...,3秒之后,数据才被加载,页面才会更新。

2、异步加载时的页面闪烁与内容不一致

接口请求需要时间,页面初始渲染会先使用默认数据或空值进行渲染。数据加载完成后,页面可能会发生更新,导致用户看到的内容短暂不一致或发生闪烁。

2. 解决方法

1、手动触发页面更新

如果响应式数据未触发渲染,可以尝试使用 nextTick 或通过直接重新赋值触发更新。

🌰

import { defineComponent, ref, nextTick } from 'vue';
export default defineComponent(() => {
  const message = ref<string | null>(null);
  const updateMessage = async () => {
    setTimeout(() => {
      message.value = 'Hello, Vue!';
      nextTick(() => {
        console.log('视图已更新');
      });
    }, 1000);
  };
  updateMessage();
  return () => (
    <div>
      <p>{message.value || '加载中...'}</p>
    </div>
  );
});

2、使用占位符避免初次渲染闪烁

在初次加载数据前,渲染占位符或骨架屏,确保用户体验。

🌰

import { defineComponent, ref } from 'vue';
export default defineComponent(() => {
  const loading = ref(true);
  const data = ref<string | null>(null);
  setTimeout(() => {
    data.value = '接口数据加载完成';
    loading.value = false;
  }, 2000);
  return () => (
    <div>
      {loading.value ? (
        <p>加载中...</p>
      ) : (
        <p>数据:{data.value}</p>
      )}
    </div>
  );
});

3、异步逻辑补充响应式支持

在异步接口中获取数据后,直接调用 Vue 提供的响应式 API 来强制触发更新。

🌰

import { defineComponent, reactive } from 'vue';
export default defineComponent(() => {
  const state = reactive({
    list: [] as string[],
  });
  const fetchList = async () => {
    setTimeout(() => {
      state.list = ['Item 1', 'Item 2', 'Item 3']; // 确保重新赋值触发响应式
    }, 1000);
  };
  fetchList();
  return () => (
    <div>
      <ul>
        {state.list.length > 0 ? state.list.map((item) => <li>{item}</li>) : '加载中...'}
      </ul>
    </div>
  );
});

4、使用 watch 监听数据变化并在更新时执行额外的逻辑。

🌰

import { defineComponent, ref, watch } from 'vue';
export default defineComponent(() => {
  const data = ref<string | null>(null);

  watch(data, (newVal) => {
    console.log('数据已更新:', newVal);
  });

  setTimeout(() => {
    data.value = 'Hello, World!';
  }, 1000);

  return () => (
    <div>
      <p>{data.value || '加载中...'}</p>
    </div>
  );
});

关键点:

1、确保使用响应式对象或变量,并在赋值时考虑 Vue 响应式系统的特点。

2、如果页面更新异常,可使用 nextTick 或 watch 来确保触发渲染。

3、提前处理数据加载占位,优化用户体验。

3. 新的疑惑

可能会出现一个困惑:

为什么使用 ref 的响应式数据在页面渲染后获取后,不直接更新页面,而必须放在 watch 里面呢?

ref 的响应式数据本身是具有更新页面的能力的,但如果在页面渲染后会发现数据更新未触发视图变化,可能是由于以下几个原因导致的:

1、页面未绑定响应式数据

如果在模板或渲染函数中,数据未绑定到 DOM 或组件中,Vue 的响应式系统就不会知道该数据的变化需要触发视图更新。

🌰

import { defineComponent, ref } from 'vue';
export default defineComponent(() => {
  const data = ref<string | null>(null);
  setTimeout(() => {
    data.value = '新数据'; // 更新数据
  }, 2000);
  return () => (
    <div>
      {/* 没有直接使用 data.value */}
      <p>数据加载完成!</p>
    </div>
  );
});

示例中,虽然 data.value 被更新了,但它并未绑定到页面上,因此页面没有感知到需要重新渲染。为了解决这个问题,需要确保将数据绑定到视图中:

import { defineComponent, ref } from 'vue';
export default defineComponent(() => {
  const data = ref<string | null>(null);
  setTimeout(() => {
    data.value = '新数据'; // 更新数据
  }, 2000);
  return () => (
    <div>
      <p>{data.value || '加载中...'}</p>
    </div>
  );
});

2、数据赋值操作不当

当从接口获取数据后,不正确的赋值操作,也可以造成响应式数据未更新导致的。

3、为什么放到 watch 中能解决?

watch 的作用是监听响应式数据的变化并执行相应的副作用逻辑。即使数据更新没有直接绑定到视图,watch 可以保证代码逻辑在数据变化时被触发。

🌰

import { defineComponent, ref, watch } from 'vue';
export default defineComponent(() => {
  const data = ref<string | null>(null);
  setTimeout(() => {
    data.value = 'Hello, Vue!';
  }, 2000);
  // 监听数据变化并更新额外逻辑
  watch(data, (newVal) => {
    console.log('数据更新为:', newVal);
  });
  return () => (
    <div>
      <p>{data.value || '加载中...'}</p>
    </div>
  );
});

原因:watch 主动监听数据变化,无论视图是否绑定响应式数据,watch 都会响应数据变化并执行逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

跨平台WPF框架Avalonia教程 一

安装 安装 Avalonia UI 模板​ 开始使用 Avalonia 的最佳方式是使用模板创建一个应用程序。 要安装 Avalonia 模板&#xff0c;请运行以下命令&#xff1a; dotnet new install Avalonia.Templates 备注 对于 .NET 6.0 及更早版本&#xff0c;请将 install 替换为 --inst…

JSON.stringify的应用说明

前言 JSON.stringify() 方法将 JavaScript 对象转换为字符串,在日常开发中较常用&#xff0c;但JSON.stringify其实有三个参数&#xff0c;后两个参数&#xff0c;使用较少&#xff0c;今天来介绍一下后两个参数的使用场景和示例。 语法及参数说明 JSON.stringify()&#xf…

家庭网络常识:猫与路由器

这张图大家应该不陌生——以前家庭网络的连接方式。 图1 家庭网络连接示意图 来说说猫/光猫&#xff1a; 先看看两者的图片。 图2 猫 图3 光猫 这个东西因为英文叫“modem”&#xff0c;类似中文的“猫”&#xff0c;所以简称“猫”。 猫和光猫的区别就是&#xff0c;一…

core 不可变类型 线程安全 record

当一个类型的对象在创建时被指定状态后&#xff0c;就不会再变化的对象&#xff0c;我们称之为不可变类型。这种类型是线程安全的&#xff0c;不需要进行线程同步&#xff0c;非常适合并行计算的数据共享。它减少了更新对象会引起各种bug的风险&#xff0c;更为安全。 System.D…

机器学习 ---线性回归

目录 摘要&#xff1a; 一、简单线性回归与多元线性回归 1、简单线性回归 2、多元线性回归 3、残差 二、线性回归的正规方程解 1、线性回归训练流程 2、线性回归的正规方程解 &#xff08;1&#xff09;适用场景 &#xff08;2&#xff09;正规方程解的公式 三、衡量…

基于Java Springboot甘肃旅游管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

uniApp项目运行到鸿蒙手机,应用图标一直是H,应用名一直是HBuilder问题

项目运行到鸿蒙手机&#xff0c;应用图标一直是H,应用名一直是HBuilder问题 应用运行到鸿蒙手机和鸿蒙模拟器&#xff0c;应用图标一直是H,应用名一直是HBuilder&#xff0c;在自动生成的harmony-configs文件夹下也没有配置的文件&#xff0c; 这时候需要你将DevEco Studio 下…

Spring:IOC/DI注解开发管理第三方bean

前面定义bean的时候都是在自己开发的类上面写个注解就完成了&#xff0c;但如果是第三方的类&#xff0c;这些类都是在jar包中&#xff0c;我们没有办法在类上面添加注解&#xff0c;这个时候该怎么办? 遇到上述问题&#xff0c;我们就需要有一种更加灵活的方式来定义bean,这…

单片机学习笔记 5. 数码管静态显示

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~ 目录 0、实现的功能 1、Keil工程 1-1 数码管显示原理 1-2 静态与动态显示 1-3 74HC573锁存器的工作原理 1-…

使用Ollama和Open WebUI管理本地开源大模型

Open WebUI和Ollama介绍 Open WebUI 是一个功能丰富且用户友好的自托管 Web 用户界面&#xff08;WebUI&#xff09;&#xff0c;它被设计用于与大型语言模型&#xff08;LLMs&#xff09;进行交互&#xff0c;特别是那些由 Ollama 或与 OpenAI API 兼容的服务所支持的模型。O…

Debezium-EmbeddedEngine

提示&#xff1a;一个嵌入式的Kafka Connect源连接器的工作机制 文章目录 前言一、控制流图二、代码分析 1.构造函数2.完成回调3.连接器回调4.RUN总结 前言 工作机制&#xff1a; * 独立运行&#xff1a;嵌入式连接器在应用程序进程中独立运行&#xff0c;不需要Kafka、Kafka C…

【网络安全】SSL(二):Keyless SSL技术细节

未经许可,不得转载。 文章目录 TLS双重目标握手过程是什么?TLS 中的握手类型TLS 术语表RSA 握手协议临时 Diffie-Hellman 握手Diffie-Hellman 握手过程保护密钥服务器其他安全考虑性能提升场景分析持久连接精简握手会话恢复的问题Keyless SSL 的会话恢复功能会话票据恢复会话…

vue2侧边导航栏路由

<template><div><!-- :default-active"$route.path" 和index对应其路径 --><el-menu:default-active"active"class"el-menu-vertical-demo"background-color"#545c64"text-color"#fff"active-text-col…

ChatGPT Search VS Kimi探索版:AI搜索哪家强?!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

交换机配置从IP(Switch Configuration from IP)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

【Redis】基于Redis实现秒杀功能

业务的流程大概就是&#xff0c;先判断优惠卷是否过期&#xff0c;然后判断是否有库存&#xff0c;最好进行扣减库存&#xff0c;加入全局唯一id&#xff0c;然后生成订单。 一、超卖问题 真是的场景下可能会有超卖问题&#xff0c;比如开200个线程进行抢购&#xff0c;抢100个…

STL——vector(1)

博客ID&#xff1a;LanFuRenC系列专栏&#xff1a;C语言重点部分 C语言注意点 C基础 Linux 数据结构 C注意点 今日好题 声明等级&#xff1a;黑色->蓝色->红色 欢迎新粉加入&#xff0c;会一直努力提供更优质的编程博客&#xff0c;希望大家三连支持一下啦 目录 尾…

【东莞石碣】戴尔R740服务器维修raid硬盘问题

1&#xff1a;石碣某塑料工厂下午报修一台戴尔R740服务器硬盘故障&#xff0c;催的还比较着急。 2&#xff1a;工程师经过跟用户确认故障的问题以及故障服务器型号和故障硬盘型号&#xff0c;产品和配件确认好后&#xff0c;公司仓库确认有该款硬盘现货&#xff0c;DELL 12T S…

使用 .NET 创建新的 WPF 应用

本教程介绍如何使用 Visual Studio 创建新的 Windows Presentation Foundation &#xff08;WPF&#xff09; 应用。 使用 Visual Studio&#xff0c;可以向窗口添加控件以设计应用的 UI&#xff0c;并处理这些控件中的输入事件以与用户交互。 在本教程结束时&#xff0c;你有一…

Shell基础(5)

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…