Vue3 v-for:常用用法与开发高频坑点

前言:

v-for 是 Vue3 中最高频的列表渲染指令,要写 Vue 页面,几乎天天都要和 v-for 打交道:表格、下拉选项、卡片列表、动态表单...... 全靠它循环渲染。很多人觉得 v-for 很简单,但是开发中经常遇到:列表数据错乱、视图不更新、控制台报错、输入框内容串值等玄学问题。 这些问题大多不是 Vue bug,而是 v-for 使用细节没注意。

一、v-for 常用用法:

1. 遍历数组(最常用)

语法:v-for="(item, index) in array"

  • item:数组当前项
  • index:当前下标索引
xml 复制代码
<template>
  <ul>
    <li v-for="(item, index) in userList" :key="item.id">
      {{ index + 1 }}. {{ item.name }} - {{ item.age }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'
const userList = ref([
  { id: 1, name: '张三', age: 22 },
  { id: 2, name: '李四', age: 25 },
  { id: 3, name: '王五', age: 21 }
])
</script>

2. 遍历对象

语法:v-for="(value, key, index) in object"

顺序记住:值、键名、索引

xml 复制代码
<template>
  <div v-for="(val, key, idx) in info" :key="key">
    {{ idx }} | {{ key }}:{{ val }}
  </div>
</template>

<script setup>
import { reactive } from 'vue'
const info = reactive({
  name: '小明',
  gender: '男',
  address: '上海'
})
</script>

3. 遍历数字,循环固定次数

可以直接传入数字,从 1 开始循环:

xml 复制代码
<!-- 输出:1 2 3 4 5 -->
<span v-for="n in 5" :key="n">{{ n }}</span>

适合做分页页码、固定数量占位骨架屏。

4. template 循环,不生成多余 DOM

<template> 是占位标签,渲染后不会生成真实 DOM,想要循环多个标签又不想外层包一层 div,就用它:

xml 复制代码
<template>
  <template v-for="item in goodsList" :key="item.id">
    <p class="title">{{ item.title }}</p>
    <p class="price">¥{{ item.price }}</p>
  </template>
</template>

5. 循环自定义组件

循环组件和普通标签写法一致,key 同样不能少,props 正常传递:

ini 复制代码
<GoodsCard
  v-for="item in goodsList"
  :key="item.id"
  :goods="item"
/>

6. 列表过滤:优先使用 computed

不要直接在 v-for 里写过滤逻辑,模板里尽量简洁,复杂过滤交给计算属性,缓存性能更好:

xml 复制代码
<template>
  <li v-for="item in hotList" :key="item.id">{{ item.title }}</li>
</template>

<script setup>
import { ref, computed } from 'vue'
const goodsList = ref([
  { id: 1, title: '商品A', hot: true },
  { id: 2, title: '商品B', hot: false },
  { id: 3, title: '商品C', hot: true }
])
// 只展示热门商品
const hotList = computed(() => {
  return goodsList.value.filter(item => item.hot)
})
</script>

二、开发高频坑点 + 解决方案:

坑点 1:v-for 和 v-if 写在同一个标签上(头号大坑),Vue3 官方文档明确不建议 v-for 和 v-if 共存于同一个元素。

❌ 错误写法:

xml 复制代码
<!-- 不推荐!v-for 优先级高于 v-if,会先循环全部再判断,性能差,逻辑容易出问题 -->
<li v-for="item in list" v-if="item.isShow" :key="item.id">
  {{ item.name }}
</li>

两种推荐方案:

1. template 包裹,v-for 放外层 template

ini 复制代码
<template v-for="item in list" :key="item.id">
  <li v-if="item.isShow">{{ item.name }}</li>
</template>

2. 优先 computed 提前过滤数组(性能最优)

坑点 2:key 绑定 index,列表增删、拖拽后数据 DOM 错位

❌ 错误写法:

css 复制代码
<li v-for="(item, index) in list" :key="index">{{ item.name }}</li>

问题点: 当数组中间删除、插入、拖拽排序时,index 会重新变化,Vue Diff 算法会复用错误 DOM,输入框内容串掉、数据和界面对不上,这个坑在带表单的列表里非常致命。

推荐写法:

使用业务唯一 ID

css 复制代码
<li v-for="item in list" :key="item.id">{{ item.name }}</li>

只有一种特殊情况可以用 index:列表是静态写死的,永远不会新增、删除、排序。业务动态列表一律不要用 index 做 key。

坑点 3:直接通过数组下标赋值,视图不更新

❌ 容易失效写法

css 复制代码
// 直接下标赋值,Vue3 Proxy 虽然比Vue2好,但复杂场景依然会出现不更新
list.value[0] = { id: 99, name: '测试' }

解决办法:

  1. 使用 splice 修改
php 复制代码
list.value.splice(0, 1, { id: 99, name: '测试' })
  1. 替换数组引用
ini 复制代码
list.value = [...list.value]

坑点 4:key 值重复,控制台警告,渲染错乱

同一个循环内 key 重复,Vue 抛出警告,DOM 随机错乱,很难复现排查。

xml 复制代码
<!-- 两条数据id相同,key重复 -->
<li v-for="item in list" :key="item.id">{{ item.name }}</li>

原因:后端返回数据 id 重复、模拟数据写重了。

解决:保证 key 在当前循环内唯一;没有唯一 id 时前端生成 uuid。

坑点 5:循环组件收集 ref,直接 ref 拿不到组件实例

❌ 错误写法

ini 复制代码
<MyInput v-for="item in list" :key="item.id" ref="inputRef" />

这样写 inputRef 只会拿到最后一个组件实例。

推荐写法:

Vue3 使用函数式 ref 收集

xml 复制代码
<MyInput v-for="item in list" :key="item.id" :ref="setRef" />

<script setup>
const compList = []
const setRef = (el) => {
  if (el) compList.push(el)
}
</script>

坑点 6:循环输入框 v-model 绑定同一个变量,全部联动

❌ 错误:

ini 复制代码
<input v-for="item in list" :key="item.id" v-model="inputVal" />

所有输入框绑定同一个变量,改一个全部一起变。

推荐写法:

绑定循环项自身属性

ini 复制代码
<input v-for="item in list" :key="item.id" v-model="item.name" />

坑点 7:异步接口数据,初始值为 null /undefined,页面直接报错

❌ 错误:

接口还没返回,list 默认是 null,v-for 循环会报迭代器错误:Cannot read property 'Symbol(Symbol.iterator)' of null

css 复制代码
<li v-for="item in list" :key="item.id">{{ item.name }}</li>

推荐方案:

初始值给空数组

csharp 复制代码
const list = ref([])
// 接口返回后再赋值

或者增加空状态判断

xml 复制代码
<ul v-if="list.length">
  <li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
<div v-else>暂无数据</div>

坑点 8:遍历响应式对象,动态新增属性视图不更新

ini 复制代码
const user = reactive({ name: '张三' })
// 直接新增属性,部分场景不触发更新
user.age = 20

解决办法:

替换对象引用

css 复制代码
Object.assign(user, { age: 20 })
// 或者
const newUser = { ...user, age: 20 }
Object.assign(user, newUser)

三、最佳实践总结:

  1. v-for 必须携带 key,优先业务唯一 ID,尽量避免 index
  2. 不要把 v-for 和 v-if 放在同一个标签;过滤列表优先 computed
  3. 尽量避免直接下标修改数组,优先 splice 或者替换引用
  4. 异步列表初始值设置为空数组,防止渲染报错
  5. 需要收集循环组件 ref,使用函数 ref
  6. template 标签用来分组渲染,减少多余 DOM
  7. 模板逻辑保持简洁,复杂计算、过滤交给 script

v-for 看着简单,但是很多坑都是上线操作增删排序的时候才暴露,尤其是动态表单、可拖拽列表场景。只要把 key、v-if 和 v-for 的优先级、响应式更新这三点把控好,基本能避开绝大多数问题。

相关推荐
coding漫漫长路1 小时前
主图纯白检测,阈值放宽到250会放过252的底
前端
志尊宝1 小时前
AI原生前端开发(017):端侧AI与Vue 3结合实战
前端·人工智能·prompt·1024·ai-native
特立独行的猫A1 小时前
一份代码,多端产物:仓颉语言的cj-tauri 框架开发桌面和鸿蒙应用指南
前端
代码猎人1 小时前
前端怎么精通Vue3核心原理
前端
不可能片场1 小时前
工程实践 台账备份从全量复制改成增量日志
前端·electron
2603_965896621 小时前
CSS BEM 命名规范|组件化样式分层权威指南
前端·javascript·css
进击的明明1 小时前
前端开发中的 BEM 国际命名规范:让 CSS 不再"打架"
前端·css·html
deli0071 小时前
中文在网址里为什么变成 E4B8AD?URL 编码 22 组用例实测
前端
羲云1 小时前
MOSS-Image 开源后爆火:5B 生图模型真正值得关注的,是可控排版
前端