Vue3 的列表渲染通过 v-for 指令实现,基于数组或可迭代对象生成重复元素。推荐使用动态唯一键值以优化组件更新和避免不必要的 DOM 操作。数组变化时,Vue3 能智能地最小化变更 DOM。列表中的响应式对象内部属性改变也会触发视图更新。
我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名,同时也支持获取索引值 index。
基础语法
// 响应式用户列表数据源
const userList = ref([{ id: 1, account: 'admin1' }, { id: 2, account: 'admin2' }])<li v-for="item in userList" :key="item.id">
{{ item.account }}
</li>代码示例
<template>
<div class="user-box">
<h3>用户列表</h3>
<ul>
<li v-for="(item, index) in userList" :key="item.id">
索引:{{ index }} | 用户 ID:{{ item.id }} | 账号:{{ item.account }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 定义用户类型接口
interface User {
id: number
account: string
}
// 统一维护一份响应式用户数组
const userList = ref<User[]>([
{ id: 1, account: 'admin1' },
{ id: 2, account: 'admin2' }
])
</script>
<style scoped>
.user-box { padding: 16px; }
li { margin: 8px 0; }
</style>定义响应式数据:
使用 ref 创建响应式数组 userList,搭配 TS 接口规范数据类型
直接初始化用户数据,结构统一、便于后续扩展
列表渲染:
使用 v-for 遍历数组,同时获取迭代项与索引
绑定数据唯一 id 作为 key,保证列表更新精准高效
与模板上的 v-if 类似,你也可以在 <template> 标签上使用 v-for 渲染包含多个元素的块,<template> 不会被渲染为真实 DOM,适合批量渲染一组关联元素。
<template>
<div class="user-box">
<h3>用户详情列表</h3>
<ul>
<!-- 延续同一数据源,批量渲染用户信息组 -->
<template v-for="item in userList" :key="item.id">
<li>用户 ID:{{ item.id }}</li>
<li>用户账号:{{ item.account }}</li>
<li class="line">——————————</li>
</template>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface User {
id: number
account: string
}
// 沿用同一份用户数据源
const userList = ref<User[]>([
{ id: 1, account: 'admin1' },
{ id: 2, account: 'admin2' }
])
</script>
<style scoped>
.user-box { padding: 16px; }
li { margin: 4px 0; }
.line { list-style: none; color: #ccc; }
</style>同时使用 v-if 和 v-for 是不推荐的,Vue3 中 v-for 优先级高于 v-if,同节点使用会造成性能浪费且逻辑混乱。
<template>
<ul>
<!-- 同节点使用 v-for 与 v-if,优先级混乱、性能差 -->
<li v-for="todo in todoList" v-if="!todo.isComplete" :key="todo.id">
{{ todo.content }}
</li>
</ul>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Todo {
id: number
content: string
isComplete: boolean
}
// 统一待办数据源
const todoList = ref<Todo[]>([
{ id: 1, content: '完成 Vue 文档编写', isComplete: false },
{ id: 2, content: '整理列表渲染案例', isComplete: true }
])
</script>在外层包装 <template> 执行遍历,内部节点使用 v-if 做条件判断,逻辑清晰且符合规范。
<template>
<div class="todo-box">
<h3>未完成任务</h3>
<ul>
<!-- 沿用同一份待办数据,逻辑连贯 -->
<template v-for="todo in todoList" :key="todo.id">
<li v-if="!todo.isComplete">{{ todo.content }}</li>
</template>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Todo {
id: number
content: string
isComplete: boolean
}
const todoList = ref<Todo[]>([
{ id: 1, content: '完成 Vue 文档编写', isComplete: false },
{ id: 2, content: '整理列表渲染案例', isComplete: true }
])
</script>
</script>
<style scoped>
.todo-box { padding: 16px; }
</style>Vue 默认按照“就地更新”策略更新 v-for 渲染的元素列表,为跟踪节点标识、重用与排序元素,必须为每个项提供唯一 key 属性。
<template>
<div class="user-box">
<!-- 完全沿用前文用户数据源,key 绑定唯一 id -->
<div v-for="item in userList" :key="item.id" class="user-item">
用户 ID:{{ item.id }},账号:{{ item.account }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface User {
id: number
account: string
}
const userList = ref<User[]>([
{ id: 1, account: 'admin1' },
{ id: 2, account: 'admin2' }
])
</script>使用 <template v-for> 时的 key
<template>
<ul>
<template v-for="todo in todoList" :key="todo.id">
<li>{{ todo.content }}</li>
</template>
</ul>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Todo {
id: number
content: string
isComplete: boolean
}
const todoList = ref<Todo[]>([
{ id: 1, content: '完成 Vue 文档编写', isComplete: false },
{ id: 2, content: '整理列表渲染案例', isComplete: true }
])
</script>Vue 能够侦听响应式数组的变更方法,并在调用时触发视图更新
这些变更方法包括:
push:向数组末尾添加元素
// 向用户列表追加新数据,视图自动更新
userList.value.push({ id: 3, account: 'admin3' })pop:移除数组最后一个元素
// 删除最后一位用户
userList.value.pop()shift:移除数组第一个元素
// 删除首位用户
userList.value.shift()unshift:向数组开头添加元素
// 在列表开头添加用户
userList.value.unshift({ id: 0, account: 'superAdmin' })splice:删除、插入或替换元素
// 从索引 1 开始删除 1 个元素
userList.value.splice(1, 1)sort:数组排序
// 按用户 id 升序排列
userList.value.sort((a, b) => a.id - b.id)reverse:颠倒数组顺序
// 反转用户列表顺序
userList.value.reverse()代码案例
<template>
<div class="user-box">
<h3>数组方法操作列表</h3>
<button @click="addUser">push 追加用户</button>
<ul>
<li v-for="item in userList" :key="item.id">{{ item.account }}</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface User {
id: number
account: string
}
// 全程使用同一份用户数据源
const userList = ref<User[]>([
{ id: 1, account: 'admin1' },
{ id: 2, account: 'admin2' }
])
// 定义方法操作数组,案例完整连贯
const addUser = () => {
userList.value.push({
id: userList.value.length + 1,
account: `admin${userList.value.length + 1}`
})
}
</script>