VueJS - 渲染
VueJS - 条件渲染与列表渲染
Section titled “VueJS - 条件渲染与列表渲染”本章介绍了如何在 Vue 中根据 JavaScript 表达式有条件地渲染元素,以及如何动态地渲染列表项。
Vue 提供了像 v-if、v-else、v-else-if 和 v-show 这样的指令,用于根据 JavaScript 表达式的真假值来控制元素的渲染或显示。
v-if, v-else, v-else-if
Section titled “v-if, v-else, v-else-if”v-if 有条件地渲染一个元素或模板片段。只有当指令的表达式评估为真值时,该元素及其内容才会被渲染。v-else 为 v-if 提供一个“else”块,而 v-else-if 提供一个“else if”块。
关键在于,v-else 和 v-else-if 元素必须紧接在一个 v-if 或 v-else-if 元素之后。
示例:使用 v-if, v-else-if, v-else
Section titled “示例:使用 v-if, v-else-if, v-else”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Conditional Rendering</title> <script src="https://unpkg.com/vue@3"></script> <style> button { margin: 5px; padding: 8px 12px; } p { margin-top: 10px; font-weight: bold; } </style></head><body> <div id="app"> <button @click="score = 95">Set Score High</button> <button @click="score = 70">Set Score Medium</button> <button @click="score = 40">Set Score Low</button> <button @click="score = null">Clear Score</button>
<p v-if="score === null"> Score not set. </p> <p v-else-if="score >= 90"> Grade: A (Score: {{ score }}) </p> <p v-else-if="score >= 60"> Grade: C (Score: {{ score }}) </p> <p v-else> Grade: F (Score: {{ score }}) </p>
<!-- 在 <template> 上使用 v-if 进行分组 --> <template v-if="score !== null && score < 60"> <p style="color: red;">This requires attention!</p> </template>
</div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const score = ref(null); return { score }; } }).mount('#app'); </script></body></html>在此示例中:
- 我们使用
ref来保存score。 - 按钮修改
score的值。 v-if/v-else-if/v-else链根据分数显示相应的等级。- 在任何时候,只有链中的一个块会被渲染。
<template>标签与v-if一起使用,用于有条件地渲染一组元素(在此例中只有一个段落),而不会在 DOM 中额外渲染一个包裹元素。
尝试点击按钮。你会看到输出文本根据 score 的变化而改变。使用浏览器开发者工具检查 DOM:注意当条件改变时,<p> 标签是如何从 DOM 结构中实际添加或移除的。
v-show
Section titled “v-show”v-show 也根据真/假表达式有条件地显示元素。与 v-if 的主要区别在于,带有 v-show 的元素总是会被渲染到 DOM 中。它只是简单地通过 CSS 的 display 属性进行切换(当条件为 false 时,添加 display: none;)。
示例:使用 v-show
Section titled “示例:使用 v-show”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 v-show Example</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <button @click="isVisible = !isVisible"> Toggle Visibility (v-show) </button>
<h2 v-show="isVisible" style="color: blue;"> This element uses v-show. It's always in the DOM. </h2>
<button @click="isRendered = !isRendered"> Toggle Rendering (v-if) </button> <h2 v-if="isRendered" style="color: green;"> This element uses v-if. It's added/removed from the DOM. </h2> </div>
<script> const { createApp, ref } = Vue;
createApp({ setup() { const isVisible = ref(true); const isRendered = ref(true); return { isVisible, isRendered }; } }).mount('#app'); </script></body></html>切换这两个按钮并检查浏览器开发者工具中的元素:
v-show元素(蓝色文本)将始终存在于 DOM 结构中,但当isVisible为false时,它会添加内联样式style="display: none;"。v-if元素(绿色文本)当isRendered为false时,将完全从 DOM 结构中消失。
v-if 与 v-show:何时使用?
Section titled “v-if 与 v-show:何时使用?”v-if的切换开销更高,因为它涉及从 DOM 添加/移除元素。它的初始渲染开销较低,因为如果条件最初为 false,则不会渲染该元素。当条件不常改变时,或当你想确保内部的组件/元素在切换时真正被销毁和重新创建时,请使用v-if。v-show的切换开销较低,因为它只更改 CSSdisplay属性。它的初始渲染开销较高,因为元素总是会被渲染。当你需要频繁切换某个元素时,请使用v-show。
常见陷阱:v-show 不能用于 <template> 元素,也不能与 v-else 或 v-else-if 一起使用。
列表渲染:使用 v-for
Section titled “列表渲染:使用 v-for”v-for 指令用于根据数组、对象、范围或其它可迭代源来渲染列表项。
- 遍历数组:
v-for="item in items"或v-for="(item, index) in items" - 遍历对象:
v-for="value in myObject"或v-for="(value, key) in myObject"或v-for="(value, key, index) in myObject" - 遍历范围:
v-for="n in 10"(渲染 1, 2, …, 10)
在 v-for 的作用域内,你可以完全访问父级作用域的属性以及当前项(item、value、n)以及可选的索引或键。
示例:渲染水果列表
Section titled “示例:渲染水果列表”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 List Rendering</title> <script src="https://unpkg.com/vue@3"></script> <style> input { margin-bottom: 10px; padding: 5px; } ul { list-style: none; padding: 0; } li { background-color: #eee; margin: 5px; padding: 10px; border-radius: 4px; } li span { font-weight: bold; margin-right: 5px; } </style></head><body> <div id="app"> <input type="text" v-model="newFruit" @keyup.enter="addFruit" placeholder="Enter a fruit and press Enter">
<h2>My Fruits:</h2> <p v-if="fruits.length === 0">No fruits yet!</p> <ul v-else> <!-- Using item and index --> <li v-for="(fruit, index) in fruits" :key="fruit.id"> <span>{{ index + 1 }}.</span> {{ fruit.name }} <button @click="removeFruit(fruit.id)" style="float: right;">X</button> </li> </ul>
<h2>Object Iteration:</h2> <ul> <li v-for="(value, key) in fruitInfo" :key="key"> <span>{{ key }}:</span> {{ value }} </li> </ul>
</div>
<script> const { createApp, ref, reactive } = Vue;
createApp({ setup() { const fruits = ref([ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Orange' } ]); const newFruit = ref(''); let nextId = 4;
const fruitInfo = reactive({ color: 'Red', taste: 'Sweet', season: 'Autumn' });
const addFruit = () => { if (newFruit.value.trim()) { fruits.value.push({ id: nextId++, name: newFruit.value.trim() }); newFruit.value = ''; // Clear input } };
const removeFruit = (idToRemove) => { fruits.value = fruits.value.filter(fruit => fruit.id !== idToRemove); };
return { fruits, newFruit, addFruit, removeFruit, fruitInfo }; } }).mount('#app'); </script></body></html>在此示例中:
- 我们使用
v-for="(fruit, index) in fruits"来遍历fruits数组。 - 我们显示
index(加 1 以便人类阅读)和fruit.name。 - 我们使用
v-for="(value, key) in fruitInfo"来遍历fruitInfo对象的属性。 - 输入框允许向响应式的
fruits数组添加新的水果,列表会自动更新。
key 的重要性
Section titled “key 的重要性”在使用 v-for 时,强烈建议为每个项提供一个唯一的 key 属性::key="uniqueValue"。这有助于 Vue 高效地识别每个节点,使其能够重用和重新排序现有元素,而不是从头创建它们。
key 为什么重要?
- 性能 (Performance): 允许 Vue 的 diff 算法在列表改变(添加、移除或重新排序项)时更加高效。
- 状态保留 (State Preservation): 有助于保留列表中组件或元素的状态。没有 key,Vue 可能会为不同的项重用一个元素,导致意外的状态。
- 过渡 (Transitions): 对于使用
<TransitionGroup>正确应用过渡至关重要(将在“过渡与动画”章节中介绍)。
请使用来自数据中的唯一标识符(例如数据库中的 ID)作为 key。避免使用循环索引作为 key,除非列表项的顺序不会改变,或者项不会从中间添加/移除,否则这会失去高效跟踪的目的。
常见陷阱:忘记 :key 属性或使用非唯一值(例如当顺序改变时使用索引)可能导致渲染错误和性能问题。
结合使用 v-for 与 v-if
Section titled “结合使用 v-for 与 v-if”通常不建议在同一个元素上同时使用 v-if 和 v-for,因为隐式的优先级可能会令人困惑(在 Vue 3 中,v-if 具有更高的优先级)。相反,你可以通过两种主要方式实现条件列表渲染:
- 过滤源数组 (Filter the source array): 使用计算属性返回过滤后的列表,然后对计算属性使用
v-for。 - 在循环内部进行条件渲染 (Conditional rendering inside the loop): 将
v-if放在v-for循环内部的一个内部元素(或<template>)上。
// 选项 1:计算属性(推荐)const visibleFruits = computed(() => fruits.value.filter(f => f.isVisible));// 模板:// <li v-for="fruit in visibleFruits" :key="fruit.id">{{ fruit.name }}</li>
// 选项 2:在 v-for 内部使用 v-if// <template v-for="fruit in fruits" :key="fruit.id">// <li v-if="fruit.isVisible">{{ fruit.name }}</li>// </template>使用计算属性过滤源数据通常更清晰且性能更好。
进一步阅读:Vue 官方文档涵盖了条件渲染:https://vuejs.org/guide/essentials/conditional.html 和列表渲染:https://vuejs.org/guide/essentials/list.html