在 Vue 中预加载组件

在 Vue 中,利用 VueRouter 可以轻松的实现两个组件(页面)之间的切换,有个常用的设计就是需要在登录页登录后跳转至一个内容页,通常的做法是在登录校验完成之后立即切换路由至内容页,接着内容页发送网络请求获取渲染需要的数据然后渲染带有业务数据的 DOM:

图片

上图中,由于内容页的核心数据都是需要通过网络请求来获取,在数据获取回来之前页面处于空白(或 loading)状态,这里并没有什么逻辑问题,只是有时候可能会想,怎么将这个等待过程提前一点比如放置路由跳转之前,让内容页的初始数据准备好了再进行路由跳转?如下图示:

图片

这篇文章的主要内容将会讨论这个问题。

方法一,数据缓存

容易想到的办法是提前据通过网将内容页的数络请求获取,待数据响应后,将获取的数据缓存至内存中,接着进行页面跳转至内容页,内容页拿到缓存的初始数据进行页面渲染,从而跳过了内容页空白(或 loading)状态

一个简单的代码实现:

// contentLogic.tsexport function loadContentRecords(params: Record<string, any>)  {// 逻辑 A// 逻辑 B// 逻辑 C// ...return axios.post('...', params)
}
<!-- Login.vue --><script setup lang="ts">
import { loadContentRecords } from './contentLogic'
import router from './router'const onSubmit = async () => {// 1. 登录await axios.post('/login', { /** ... */ })// 2. 登录通过后,预加载 content 的数据const data = await loadContentRecords({ A: false }) // ①// 3. 将预加载的数据放置在某一个地方window.data = data// 4. 数据加载完成并保存后,跳转至 content 页面router.push('/content')
}
</script><template><button @click="onSubmit">登录</button>
</template>
<!-- Content.vue --><script setup lang="ts">
import { ref } from 'vue'
import { loadContentRecords } from './contentLogic'const A = ref(false) // ②
const data = ref([])if (window.data) {// 如果有数据源,直接使用data.value = window.datadelete window.data
} else {// 否则通过接口获取loadContentRecords({A})
}
</script><template><!-- ... -->
</template>

上面的实现中,可以看出此方法有一些缺陷:

  1. 为了实现能在内容页之外预先发送网络请求来获取数据,需要将内容页的数据加载逻辑(loadContentRecords  方法)抽离至公共文件中(contentLogic.ts  ),但显然这部分逻辑不应该被多余的抽离维护成单独的文件,因为它是只属于内容页的逻辑,别的组件不会使用;
  2. 内容页组件的内部其它状态需要同步维护,在上面的 Content.vue 中,有一个默认的过滤条件 A,可以看到这个过滤条件的初始值不得不维护两次,增加了维护成本和出错概率:
    • 标记  :组件外部为了保证预加载的数据正确性,需要同步组件内部的默认过滤条件;
    • 标记  :组件内部为了配合 UI 展示,定义一个 Ref 来跟视图进行绑定。
  3. 随着“下一页面“的选择可能性变多,如可能会跳转至内容页1、内容页2... 这时每个不同选择都会有第 1、2 步,变得更加难以维护。

方法二,预加载 Vue 组件

在方法一中,导致种种缺陷的原因是我们在一个功能完整的组件中,只把其中一部分的逻辑抽离出来单独执行,且这部分逻辑丢失了组件中的上下文(如过滤条件 A,或者一些分页参数等),所以不得不再维护一份意义相同的上下文来正确执行预加载操作

方法二则通过预加载组件,在不重构组件内部逻辑的前提下来实现相同的功能

在 Vue3 中,可以通过 h  方法来创建一个 VNode ,参数是一个组件对象

import { h } from 'vue'
import Content from './Content.vue'const vnode = h(Content)

通过 render 方法将一个 VNode 渲染至 DOM 中,其中我们的目的是需要执行组件的逻辑,不需要将组件渲染进页面的 DOM 树中,因此只需要在内存中准备一个空的容器放置组件的 DOM 即可

import { h, render } from 'vue'
import Content from './Content.vue'const vnode = h(Content)
render(vnode, window.document.createElement('div'))

至此,Content 组件已经被正常加载并挂载在内存中的一个匿名 div 中,假设 Content 的组件内部的网络请求总是需要 1s 才能完成,结合方法一中的示例,修改 login.vue:

<!-- Login.vue --><script setup lang="ts">
import { h, render } from 'vue'
import Content from './Content.vue'
import router from './router'const onSubmit = async () => {// 1. 登录await axios.post('/login', { /** ... */ })// 2. 预加载 Content 组件const vnode = h(Content)render(vnode, window.document.createElement('div'))// 找个地方保存这个预加载的 VNodewindow.contentVNode = vnodewindow.setTimeout(() => {// 3. 1s 过后(上文中的约定时间),组件中的数据加载完成,跳转至 content 页面router.push('/content')}, 1000)
}
</script><template><button @click="onSubmit">登录</button>
</template>

事实上,目前仅仅是内存中加载了 Content 组件并不会有缓存的效果,因为在路由跳转后,VueRouter 又会重新渲染一个全新的 Content 组件,和我们在内存中预加载的 Content 没有任何联系

借用 KeepAlive 组件的思想,在路由跳转后渲染 Content 组件时,让 Vue 知道 “这个 Content 组件有缓存,读缓存就完事了“

给预加载的 vnode 加上有缓存标识,也就是给 vnode 的 shapeFlag 属性添加已被缓存的标识

<!-- Login.vue --><script setup lang="ts">
import { h, render } from 'vue'
import Content from './Content.vue'
import router from './router'const onSubmit = async () => {// 1. 登录await axios.post('/login', { /** ... */ })// 2. 预加载 Content 组件const vnode = h(Content)render(vnode, window.document.createElement('div'))// 找个地方保存这个预加载的 VNodewindow.contentVNode = vnode// +++++++++++++++ 这里是添加的一行,ShapeFlag 是 @vue/shared 包定义的枚举, 512 是其中的一项// +++++++++++++++ (源代码:COMPONENT_KEPT_ALIVE = 1 << 9)// +++++++++++++++ 标识这个 vnode 是有缓存的(这里实际上是借助 KeepAlive 组件的实现)vnode.shapeFlag |= 512window.setTimeout(() => {// 3. 1s 过后(上文中的约定时间),组件中的数据加载完成,跳转至 content 页面router.push('/content')}, 1000)
}
</script><template><button @click="onSubmit">登录</button>
</template>

在 RouterView 组件插槽拿到了路由匹配到的组件之后,通过自定义一个“代理”组件,来判断是否有缓存的组件可以读取

<!-- App.vue -->
<script setup lang="ts">
import { getCurrentInstance, h } from 'vue'
import type { Component } from 'vue'const MyComponent: Component = {props: ['is'],setup(props) {const instance = getCurrentInstance() as any// Vue 在对一个 VNode 进行挂载操作时,会判断此 VNode 是否有缓存(通过上面给的 "512" 标识)// 如有缓存,则会调用 VNode 的父元素此方法//(源码中这种情况父元素就是 KeepAlive,但此时借助 KeepAlive 的思想,当前这个组件也实现这个方法)// 如没有缓存,Vue 就会从 0 挂载一个组件instance.ctx.activate = (vnode: VNode, container: HTMLElement, anchor: ChildNode | null) => {// 只需要将缓存的 VNode 里的 DOM 结构插入到文档中即可container.insertBefore(vnode.component!.subTree.el! as any, anchor)}// setup 可返回一个函数,表示此组件的 render 函数return () => {const { is } = propsif (!is) return null// 找到预先加载的 VNode 了,返回这个内存中的 VNode,且这个 VNode 的 shapeFlag 是带有 “512” 标识的// 进入 Vue 后续的挂载逻辑后,就会走上面的 `activate` 方法if (window.contentVNode) return window.contentVNode// 不是缓存的 VNode,原样返回即可else return is}},
}
</script><template><RouterView v-slot="{ Component }"><MyComponent :is="Component"></MyComponent></RouterView>
</template>

至此实现完成。

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

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

相关文章

SpringBoot (批量)生成二维码工具类多种方法示例

一、引入依赖 <dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.4.1</version> </dependency><dependency><groupId>com.google.zxing</groupId><artifactId…

Jmeter05:配置环境变量

1 Jmeter 环境 1.1 什么是环境变量&#xff1f;path什么用&#xff1f; 系统设置之一&#xff0c;通过设置PATH&#xff0c;可以让程序在DOS命令行直接启动 1.2 path怎么用 如果想让一个程序可以在DOS直接启动&#xff0c;需要将该程序目录配置进PATH 1.3 PATH和我们的关系…

【自然语言处理】InstructGPT、GPT-4 概述

InstructGPT官方论文地址&#xff1a;https://arxiv.org/pdf/2203.02155.pdf GPT-4 Technical Report&#xff1a;https://arxiv.org/pdf/2303.08774.pdf GPT-4&#xff1a;GPT-4 目录 1 InstructGPT 2 GPT-4 1 InstructGPT 在了解ChatGPT之前&#xff0c;我们先看看Instr…

Three.js--》探秘虚拟现实VR展厅的视觉盛宴

今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 源码下载地址&#xff1a;地址 在线体验地址&#xff1a;地址 目录 项目搭建 初始化three代码 camera…

分布式系统事务一致性解决方案(基于事务消息)

参考&#xff1a;https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage/ 文章目录 概要错误的方案方案一&#xff1a;业务方自己实现方案二&#xff1a;RocketMQ 事务消息什么是事务消息事务消息处理流程事务消息生命周期使用限制使用示例使用建议 概要 …

单纯形投影算法

目录 一&#xff0c;任意点到平移坐标轴面的投影 1&#xff0c;求解目标 2&#xff0c;转换变量 3&#xff0c;求解结果 4&#xff0c;f(t)的导数 5&#xff0c;f(t)的最小值 二&#xff0c;任意点到标准单纯形的投影 1&#xff0c;求解目标 2&#xff0c;公式变形 3…

制作一个RISC-V的操作系统十四-任务同步和锁

文章目录 并发与同步临界区和锁锁死锁解决死锁自旋锁&#xff08;spin lock&#xff09;原子性问题原子操作实现amoswap.w.aq例子 另一种方法自旋锁的注意事项代码其他同步技术 并发与同步 控制流&#xff1a;可理解为任务或进程 中断也可以理解为一个切换到另一个任务&#…

LeetCode 226.翻转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1]示例…

CMake+qt+Visual Studio

#使用qt Creator 创建Cmake 项目,使用Cmake Gui 生成sln 工程&#xff0c;使用Visual Studio 开发 ##使用qt Creator 创建CMake项目 和创建pro工程的步骤一致&#xff0c;只是在选择构建系统的步骤上选择CMake,接下来步骤完全相同 工程新建完成之后&#xff0c;构建cmake 项…

想冲宇宙厂,直接挂了。。。

宇宙厂实际是字节&#xff0c;这个称呼是因为字节跳动主宰了宇宙内一切App&#xff0c;有点家大业大的意思。 今天分享一位字节春招凉经&#xff0c;问了一些数据库和Java八股&#xff0c;没出算法题&#xff0c;直接挂了&#xff0c;竟然最喜欢出算法题的字节&#xff0c;这次…

刷代码随想录有感(49):找树左下角的值

题干&#xff1a; 用层序遍历方便些&#xff0c;因为只需要把res不断替换成每一层第一个节点值即可&#xff0c;代码如下&#xff1a; class Solution { public:int findBottomLeftValue(TreeNode* root) {queue<TreeNode*>que;if(root ! NULL)que.push(root);int res …

C++中的数据结构与算法

随处可见的红黑树 一般会用到[key,value]。 例如github中这个例子&#xff0c;第一个是访问网站&#xff0c;第二个是访问次数&#xff0c;但是这个不是静态的&#xff0c;这有个动态排序&#xff0c;并且当我们需要让相应的访问次数加1的时候&#xff0c;我们用红黑树查找的时…

C++每日一练——杨辉三角

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]]提示: 1 <…

Jsoncpp搭建交叉编译环境(移植到arm)

1. 官网下载源码 github地址&#xff1a;GitHub - open-source-parsers/jsoncpp at update 2. 交叉编译环境 当前平台/开发平台-编译环境&#xff1a; [rootlocalroot ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalroot ~]# uname -a Lin…

ELK创建仪表盘

创建仪表盘步骤&#xff1a; 一、保存search二、生成饼图三、创建仪表盘 一、保存search 首先保存一段时间内的search&#xff0c;可以添加想要的字段&#xff0c;并保存这个search方便下次直接打开该search&#xff0c;并方便在可视化和仪表盘中使用该search. 二、生成饼图…

Mac安装telnet

一、安装Homebrew 1、打开官网&#xff1a;Homebrew — The Missing Package Manager for macOS (or Linux) 2、打开终端输入&#xff1a; /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 二、安装Telnet bre…

数字文旅重塑旅游发展新格局:以数字化转型为突破口,提升旅游服务的智能化水平,为游客带来全新的旅游体验

随着信息技术的迅猛发展&#xff0c;数字化已成为推动各行各业创新发展的重要力量。在旅游业领域&#xff0c;数字文旅的兴起正以其强大的驱动力&#xff0c;重塑旅游发展的新格局。数字文旅以数字化转型为突破口&#xff0c;通过提升旅游服务的智能化水平&#xff0c;为游客带…

急急急!微信朋友圈删除了怎么恢复?

微信朋友圈是我们与朋友分享生活点滴的重要平台&#xff0c;但有时候微信出现异常&#xff0c;导致我们编辑好的朋友圈被删除了&#xff0c;这时候该怎么办呢&#xff1f; 幸运的是&#xff0c;微信提供了一种简单的方式来恢复已删除的朋友圈内容。微信朋友圈删除了怎么恢复&a…

四信数字孪生水库解决方案,加快构建现代化水库运行管理矩阵

近年&#xff0c;水利部先后出台《关于加快构建现代化水库运行管理矩阵的指导意见》与《构建现代化水库运行管理矩阵先行先试工作方案》等文件&#xff0c;明确总体要求及试点水库、先行区域建设技术要求等&#xff0c;为全面推进现代化水库运行管理矩阵建设工作提供依据。 《2…

【Elasticsearch<一>✈️✈️】简单安装使用以及各种踩坑

目录 &#x1f378;前言 &#x1f37b;一、软件安装&#xff08;Windows版&#xff09; 1.1、Elasticsearch 下载 2.1 安装浏览器插件 3.1、安装可视化工具 Kibana 4.1、集成 IK 分词器 &#x1f37a;二、安装问题 &#x1f379;三、测试 IK 分词器 ​&#x1f377; 四、章…