Vue 条件与循环渲染:v-if/v-else 与 v-for 的语法简介

在 Vue 开发中,条件渲染(v-if/v-else)和循环渲染(v-for)是最常用的模板语法,它们让我们能够根据数据动态控制 DOM 元素的显示与列表渲染。

接下来跟随我的脚步,一起来看看这两种语法的用法、区别。

一、条件渲染:v-if /v-else/v-else-if

条件渲染用于根据表达式的真假来动态显示或隐藏元素,Vue 提供了 v-ifv-elsev-else-if 三个指令实现这一功能。

1. 基本用法

vue 复制代码
<template>
  <div class="condition-example">
    <!-- v-if:表达式为真时渲染 -->
    <p v-if="isVisible">这是 v-if 渲染的内容</p>

    <!-- v-else:与 v-if 搭配使用,当 v-if 为假时渲染 -->
    <p v-else>这是 v-else 渲染的内容</p>

    <!-- v-else-if:多条件判断 -->
    <div v-if="score >= 90">优秀</div>
    <div v-else-if="score >= 60">及格</div>
    <div v-else>不及格</div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

// 控制 v-if/v-else 的显示
const isVisible = ref(true);
// 控制多条件判断
const score = ref(85);
</script>

核心特点

  • v-if 是 "真正的条件渲染"------ 当条件为假时,元素会被完全从 DOM 中移除,而非仅隐藏;
  • v-elsev-else-if 必须紧跟在 v-ifv-else-if 后面,否则无法识别;
  • 条件表达式可以是任何返回布尔值的 JavaScript 表达式(如 score >= 90isLogin && hasPermission)。

2. v-if 与 v-show 的区别

Vue 还提供 v-show 指令用于条件显示,它与 v-if 的核心区别在于元素是否被移除

vue 复制代码
<template>
  <div>
    <!-- v-if:条件为假时元素被移除 -->
    <p v-if="isVisible">v-if 内容</p>

    <!-- v-show:条件为假时元素被隐藏(添加 display: none 样式) -->
    <p v-show="isVisible">v-show 内容</p>
  </div>
</template>
特性 v-if v-show
渲染方式 条件为真时才渲染元素 始终渲染元素,通过样式控制显示 / 隐藏
DOM 操作 会触发元素的创建 / 销毁 仅修改样式,元素始终存在
初始渲染成本 条件为假时,初始渲染成本低 无论条件真假,初始渲染成本高
切换成本 条件切换时,成本高(DOM 操作) 切换成本低(仅修改样式)
适用场景 条件很少切换(如权限判断) 条件频繁切换(如标签页切换)

3. 条件渲染的最佳实践

(1)避免在同一元素上使用 v-if 和 v-for

Vue 官方明确建议不要在同一元素上同时使用 v-ifv-for ,因为 v-for 的优先级高于 v-if,会导致性能问题(循环中每次都要判断条件)。

错误示例

vue 复制代码
<!-- 不推荐:v-for 和 v-if 同时使用 -->
<ul>
  <li v-for="item in list" v-if="item.active" :key="item.id">
    {{ item.name }}
  </li>
</ul>

正确做法:先通过计算属性过滤数据,再循环渲染:

vue 复制代码
<template>
  <ul>
    <!-- 推荐:先过滤再循环 -->
    <li v-for="item in activeItems" :key="item.id">
      {{ item.name }}
    </li>
  </ul>
</template>

<script>
import { ref, computed } from 'vue';

const list = ref([
  { id: 1, name: '选项1', active: true },
  { id: 2, name: '选项2', active: false },
  { id: 3, name: '选项3', active: true }
]);

// 计算属性过滤数据
const activeItems = computed(() => {
  return list.value.filter(item => item.active);
});
</script>

(2)使用 <template> 包裹多元素条件渲染

当需要条件渲染多个元素时,可使用 <template> 作为包裹容器(不会被渲染到 DOM 中):

vue 复制代码
<template>
  <!-- 使用 template 包裹多元素,避免额外 DOM 节点 -->
  <template v-if="hasPermission">
    <h3>权限内容标题</h3>
    <p>这是需要权限才能查看的内容</p>
    <button>操作按钮</button>
  </template>
</template>

(3)为条件渲染的元素添加 key 提升性能

当切换 v-if/v-else 时,Vue 可能会复用已有元素以优化性能。若需避免复用(如表单输入场景),可添加 key 标识:

vue 复制代码
<template>
  <div>
    <template v-if="isEditing">
      <input type="text" key="edit-input" placeholder="编辑模式">
    </template>
    <template v-else>
      <input type="text" key="view-input" placeholder="查看模式">
    </template>
    <button @click="isEditing = !isEditing">切换模式</button>
  </div>
</template>

<script>
import { ref } from 'vue';
const isEditing = ref(false);
</script>

添加不同 key 后,切换模式时输入框会被重新创建,而非复用,避免输入内容意外保留。

二、循环渲染:v-for

v-for 用于基于数组或对象渲染列表,是处理动态数据列表的核心指令。

1. 基本用法

(1)遍历数组

vue 复制代码
<template>
  <ul>
    <!-- 基本用法:item 为数组元素 -->
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
    </li>

    <!-- 带索引:(item, index) 接收元素和索引 -->
    <li v-for="(item, index) in items" :key="item.id">
      {{ index + 1 }}. {{ item.name }}
    </li>
  </ul>
</template>

<script>
import { ref } from 'vue';

const items = ref([
  { id: 1, name: '苹果' },
  { id: 2, name: '香蕉' },
  { id: 3, name: '橙子' }
]);
</script>

(2)遍历对象

vue 复制代码
<template>
  <ul>
    <!-- 遍历对象:(value, key) 接收值和键名 -->
    <li v-for="(value, key) in user" :key="key">
      {{ key }}: {{ value }}
    </li>

    <!-- 带索引:(value, key, index) 接收值、键名和索引 -->
    <li v-for="(value, key, index) in user" :key="key">
      {{ index + 1 }}. {{ key }}: {{ value }}
    </li>
  </ul>
</template>

<script>
import { reactive } from 'vue';

const user = reactive({
  name: '张三',
  age: 20,
  gender: '男'
});
</script>

(3)遍历整数

v-for 还可以直接遍历整数,用于渲染固定次数的元素:

vue 复制代码
<template>
  <div>
    <!-- 渲染 5 个星号 -->
    <span v-for="n in 5" :key="n">★</span>
  </div>
</template>

2. key 属性的重要性

v-for 渲染列表时,必须为每个项添加唯一的 key 属性,这是 Vue 虚拟 DOM Diff 算法的关键,用于识别列表项的身份,优化渲染性能。

vue 复制代码
<!-- 正确:使用唯一 ID 作为 key -->
<li v-for="item in items" :key="item.id">
  {{ item.name }}
</li>

为什么需要 key?

当列表数据变化时(如增删、排序),Vue 会通过 key 判断哪些元素是新增的、删除的或移动的,从而只更新变化的部分,而非重新渲染整个列表。

key 的选择原则

  • 优先使用数据本身的唯一标识 (如后端返回的 id);
  • 避免使用索引(index)作为 key------ 当列表排序或删除中间项时,索引会变化,导致 key 失效,反而影响性能;
  • 若数据确实没有唯一标识,可使用 Symbol 或其他方式生成临时唯一 key。

3. 数组更新检测

Vue 对数组的一些方法进行了包裹,使得这些方法修改数组后会自动触发视图更新,这些方法被称为 "响应式数组方法":

  • 修改原数组的方法(会触发更新):

    • push():添加元素到末尾
    • pop():删除末尾元素
    • shift():删除第一个元素
    • unshift():添加元素到开头
    • splice():添加 / 删除 / 替换元素
    • sort():排序
    • reverse():反转
  • 返回新数组的方法(需替换原数组才会触发更新):

    • filter()
    • concat()
    • slice()
vue 复制代码
<template>
  <div>
    <ul>
      <li v-for="item in fruits" :key="item.id">{{ item.name }}</li>
    </ul>
    <button @click="addFruit">添加水果</button>
    <button @click="filterFruits">筛选苹果</button>
  </div>
</template>

<script>
import { ref } from 'vue';

const fruits = ref([
  { id: 1, name: '苹果' },
  { id: 2, name: '香蕉' },
  { id: 3, name: '橙子' }
]);

// 使用 push() 修改原数组(自动更新视图)
const addFruit = () => {
  fruits.value.push({ id: 4, name: '草莓' });
};

// 使用 filter() 返回新数组(需替换原数组才更新视图)
const filterFruits = () => {
  fruits.value = fruits.value.filter(item => item.name === '苹果');
};
</script>

注意 :直接通过索引修改数组元素(如 fruits.value[0] = { ... })不会触发视图更新,需使用 splice 或替换整个数组:

vue 复制代码
// 错误:直接修改索引,不触发更新
fruits.value[0] = { id: 1, name: '红苹果' };

// 正确:使用 splice 修改
fruits.value.splice(0, 1, { id: 1, name: '红苹果' });

// 正确:替换整个数组
fruits.value = [...fruits.value.slice(0, 0), { id: 1, name: '红苹果' }, ...fruits.value.slice(1)];

4. 循环渲染的最佳实践

(1)始终指定 key 且确保唯一

如前所述,key 是优化列表渲染的关键,必须添加且确保唯一:

vue 复制代码
<!-- 推荐 -->
<li v-for="item in products" :key="item.id">
  {{ item.name }}
</li>

<!-- 不推荐:使用索引作为 key -->
<li v-for="(item, index) in products" :key="index">
  {{ item.name }}
</li>

(2)避免在 v-for 中修改原数据

循环渲染时,应避免直接在模板中修改数组元素(如 v-for 内部使用 v-model 绑定元素属性),推荐通过方法统一处理:

vue 复制代码
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <input 
        type="text" 
        :value="item.name" 
        @input="(e) => updateItemName(item.id, e.target.value)"
      >
    </li>
  </ul>
</template>

<script>
import { ref } from 'vue';

const items = ref([
  { id: 1, name: '商品1' },
  { id: 2, name: '商品2' }
]);

// 统一方法更新数据,逻辑更清晰
const updateItemName = (id, newName) => {
  const item = items.value.find(item => item.id === id);
  if (item) {
    item.name = newName;
  }
};
</script>

(3)使用计算属性处理列表数据

当需要对列表进行过滤、排序等操作时,优先使用计算属性处理,而非在 v-for 中直接处理,提高代码可读性和性能:

vue 复制代码
<template>
  <div>
    <input v-model="searchText" placeholder="搜索商品">
    <ul>
      <!-- 渲染过滤后的列表 -->
      <li v-for="item in filteredItems" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

const items = ref([
  { id: 1, name: '苹果手机' },
  { id: 2, name: '华为平板' },
  { id: 3, name: '小米手表' }
]);
const searchText = ref('');

// 计算属性过滤数据
const filteredItems = computed(() => {
  return items.value.filter(item => 
    item.name.toLowerCase().includes(searchText.value.toLowerCase())
  );
});
</script>

(4)控制循环渲染的范围

对于大型列表(如 1000+ 项),直接全部渲染会导致性能问题,可采用 "分页" 或 "虚拟滚动" 优化:

vue 复制代码
<!-- 分页示例 -->
<template>
  <div>
    <ul>
      <li v-for="item in currentPageItems" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <button @click="currentPage--" :disabled="currentPage === 1">上一页</button>
    <span>第 {{ currentPage }} 页</span>
    <button @click="currentPage++" :disabled="currentPage >= totalPages">下一页</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

// 模拟大量数据
const items = ref(Array.from({ length: 100 }, (_, i) => ({
  id: i + 1,
  name: `项目 ${i + 1}`
})));
const pageSize = 10; // 每页显示 10 条
const currentPage = ref(1); // 当前页码

// 计算当前页数据
const currentPageItems = computed(() => {
  const start = (currentPage.value - 1) * pageSize;
  const end = start + pageSize;
  return items.value.slice(start, end);
});

// 计算总页数
const totalPages = computed(() => {
  return Math.ceil(items.value.length / pageSize);
});
</script>

三、总结

  1. 条件渲染

    • v-if 完全移除 / 创建元素,v-show 仅隐藏 / 显示元素,根据场景选择;
    • 避免 v-ifv-for 同时使用,优先用计算属性过滤数据;
    • 多元素条件渲染用 <template> 包裹,减少额外 DOM 节点。
  2. 循环渲染

    • 始终为 v-for 项添加唯一 key,优先使用数据的唯一标识;
    • 熟悉响应式数组方法,直接修改索引不会触发更新;
    • 大型列表需分页或虚拟滚动优化性能;
    • 用计算属性处理过滤、排序等逻辑,提高代码可读性。

好了好了,Vue 条件与循环渲染分享到此结束!

相关推荐
huabuyu3 小时前
构建极致流畅的亿级数据列表
前端
小枫学幽默3 小时前
2GB文件传一半就失败?前端大神教你实现大文件秒传+断点续传
前端
ai产品老杨3 小时前
打破技术壁垒,推动餐饮食安标准化进程的明厨亮灶开源了
前端·javascript·算法·开源·音视频
文心快码BaiduComate3 小时前
来WAVE SUMMIT,文心快码升级亮点抢先看!
前端·后端·程序员
布列瑟农的星空4 小时前
html中获取容器部署的环境变量
运维·前端·后端
工会代表4 小时前
nginx配置,将前端项目配置到子路径下踩过的坑。
前端·nginx
拜无忧4 小时前
【教程】vue+vite+ts创建一个最新的高性能后台项目架构
vue.js·typescript·vite
耶耶耶1114 小时前
一文搞懂谷歌插件v3版本content_scripts、background、action(popup)、devtools_page直接的关系
前端