框架实战指南-透明元素

本文是系列文章的一部分:框架实战指南 - 基础知识

呼!上一章太精彩了。这一章我们放慢节奏吧:短小精悍。

让我们回想一下"动态 HTML""组件简介"章节,我们在其中构建了我们的File FileList组件:

html 复制代码
<!-- File.vue --><script setup>import { ref, onMounted, onUnmounted } from "vue";import FileDate from "./FileDate.vue";const props = defineProps(["isSelected", "isFolder", "fileName", "href"]);const emit = defineEmits(["selected"]);const inputDate = ref(new Date());// ...</script><template>	<button		v-on:click="emit('selected')"		:style="			isSelected				? 'background-color: blue; color: white'				: 'background-color: white; color: blue'		"	>		{{ fileName }}		<span v-if="isFolder">Type: Folder</span>		<span v-else>Type: File</span>		<FileDate v-if="!isFolder" :inputDate="inputDate" />	</button></template>
html 复制代码
<!-- FileList.vue --><script setup>// ...</script><template>	<!-- ... -->	<ul>		<li v-for="(file, i) in filesArray" :key="file.id">			<File				v-if="onlyShowFiles ? !file.isFolder : true"				@selected="onSelected(i)"				:isSelected="selectedIndex === i"				:fileName="file.fileName"				:href="file.href"				:isFolder="file.isFolder"			/>		</li>	</ul>	<!-- ... --></template>

虽然理论上可行 onlyShowFiles=true,但存在一个重大问题。让我们看一下使用以下代码渲染时的 HTML 效果filesArray

js 复制代码
[	{		fileName: "File one",		href: "/file/file_one",		isFolder: false,		id: 1,	},	{		fileName: "Folder one",		href: "",		isFolder: true,		id: 2,	},];

因为我们的条件语句是在li渲染到 DOM 时,所以它可能看起来像这样:

html 复制代码
<!-- ... --><ul>	<li>		<!-- File Component -->		<button>...</button>	</li>	<li></li></ul><!-- ... -->

虽然乍一看这似乎不是什么大问题,但事实上li我们的中间有一个空白ul,这带来了三个问题:

  1. 它将留下由您对其应用的任何样式所创建的空白空间li
  2. 任何辅助技术,例如屏幕阅读器,都会读出有一个空项目,这对于这些用户来说是一种令人困惑的行为。
  3. 任何从您的页面读取数据的搜索引擎都可能错误地认为您的列表是故意空的,从而可能影响您在网站上的排名。

解决这些问题需要"透明元素"的帮助。理想情况下,我们想要的是类似标签那样的东西,它渲染后什么也不显示。

考虑支持

这意味着如果我们可以在框架代码中生成类似以下伪语法的内容:

html 复制代码
<ul>	<nothing>		<li>			<button>...</button>		</li>	</nothing>	<nothing></nothing></ul>

我们可以将其渲染到 DOM 本身中:

html 复制代码
<ul>	<li>		<button>...</button>	</li></ul>

幸运的是,这三个框架都提供了实现这一点的方法,只是语法不同。让我们看看每个框架是如何实现的:

为了呈现类似于nothing元素的东西,我们可以使用template带有v-forv-if与之关联的元素。

html 复制代码
<template>	<ul>		<template v-for="(file, i) of filesArray" :key="file.id">			<li v-if="onlyShowFiles ? !file.isFolder : true">				<File					@selected="onSelected(i)"					:isSelected="selectedIndex === i"					:fileName="file.fileName"					:href="file.href"					:isFolder="file.isFolder"				/>			</li>		</template>	</ul></template>

堆叠透明元素

需要简单说明的是,这些元素不仅可以nothing使用一次,而且可以连续堆叠在一起来做......嗯,没什么!

以下是一些呈现以下内容的代码示例:

css 复制代码
<p>Test</p>

虽然其他框架与我们的伪语法有更 1:1 的映射nothing,但 Vue 由于重用了现有的 HTML<template>标签,因此采用了略有不同的方法。

默认情况下,如果您在 Vue 中除根之外的任何其他位置渲染template,它将不会在屏幕上渲染任何内容:

html 复制代码
<template>	<template>		<p>Test</p>	</template></template>

值得一提的是,即使屏幕上什么都不显示,该template元素仍然位于 DOM 本身中,等待以其他方式使用。虽然解释 HTMLtemplate元素默认不渲染"什么"超出了本书的讨论范围,但这是预期的行为。

但是,如果添加v-forv-if或(我们将在"访问子项"一章v-slot介绍什么是),它将删除并仅渲染子项。v-slot<template>

这意味着:

html 复制代码
<template>	<template v-if="true">		<p>Test</p>	</template></template>

和:

html 复制代码
<template>	<template v-if="true">		<template v-if="true">			<template v-if="true">				<p>Test</p>			</template>		</template>	</template></template>

都将呈现以下 HTML:

css 复制代码
<p>Test</p>

当然,这些规则不适用于根级template,它充当模板代码的容器。一开始可能会有点困惑,但多加练习就会明白。

挑战

现在我们了解了如何渲染透明元素(无论如何对 DOM 透明),让我们构建一个有用的例子。

也就是说,假设我们想要构建一个按钮栏,按钮之间有间隙:

要使用 HTML 执行此操作,我们可能有以下模板和样式:

html 复制代码
<div	style="    display: 'inline-flex',	gap: 1rem;  ">	<button>Delete</button>	<button>Copy</button>	<button>Favorite</button>	<button>Settings</button></div>

但是,如果我们只想显示前三个按钮该怎么办?

  • 删除
  • 复制
  • 最喜欢的

仅当选择文件时?

让我们使用我们最喜欢的框架来构建它:

html 复制代码
<!-- FileActionButtons.vue --><script setup>const emit = defineEmits(["delete", "copy", "favorite"]);</script><template>	<div>		<button @click="emit('delete')">Delete</button>		<button @click="emit('copy')">Copy</button>		<button @click="emit('favorite')">Favorite</button>	</div></template>
html 复制代码
<!-- ButtonBar.vue --><script setup>import FileActionButtons from "./FileActionButtons.vue";const props = defineProps(["fileSelected"]);const emit = defineEmits(["delete", "copy", "favorite", "settings"]);</script><template>	<div style="display: flex; gap: 1rem">		<FileActionButtons			v-if="props.fileSelected"			@delete="emit('delete')"			@copy="emit('copy')"			@favorite="emit('favorite')"		/>		<button @click="emit('settings')">Settings</button>	</div></template>

糟糕!渲染结果和我们预期的不一样!

div这是因为当我们在组件中使用 时FileActionButtons,它绕过了gapCSS 的属性。为了解决这个问题,我们可以使用方便的nothing元素:

因为 Vue 的根<template>可以支持多个元素,而不需要v-ifv-forv-slot,所以我们可以执行以下操作:

html 复制代码
<!-- FileActionButtons.vue --><template>	<button @click="emit('delete')">Delete</button>	<button @click="emit('copy')">Copy</button>	<button @click="emit('favorite')">Favorite</button></template><!-- ... -->
相关推荐
小白阿龙8 分钟前
如何实现缓存音频功能(App端详解)
前端·css·html5
zhongzx10 分钟前
【HarmonyOS6】获取华为用户信息
前端
baozj10 分钟前
html2canvas + jspdf 前端PDF分页优化方案:像素级分析解决文字、表格内容截断问题
前端·vue.js·开源
可乐拌花菜14 分钟前
Vue3 + Pinia:子组件修改 Pinia 数据,竟然影响了原始数据?
前端·vue.js
siwangqishiq216 分钟前
Vulkan Tutorial 教程翻译(十二)载入模型
前端
90后的晨仔21 分钟前
🧱 《响应式基础》— Vue 如何追踪你的数据变化?
前端·vue.js
smile boy32 分钟前
个人财务记录应用
前端·javascript·css·css3·html5
hqxstudying37 分钟前
J2EE模式---业务代表模式
java·前端·python·设计模式·java-ee·mvc