Skip to content

v-for 的本质

v-for 是 Vue 模板里最常见的列表渲染能力。

如果只看表面,它像是在模板里“循环输出多个节点”;但更准确的说法是:

v-for 的本质,是在编译阶段把模板改写成列表渲染逻辑,再由运行时根据数据生成一组 VNode,并结合 key 和 diff 机制高效更新这组节点。

它不是“模板里自带一个循环语法”,而是 Vue 编译器帮你把循环意图翻译成渲染函数。

先看最基础的写法

html
<ul>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>

这里的语义很清楚:

  • 遍历 items
  • 每一项生成一个 <li>
  • key 用来标识每个节点身份

编译后的本质

这段模板大致会被编译成:

js
import {
  createElementBlock as _createElementBlock,
  Fragment as _Fragment,
  openBlock as _openBlock,
  renderList as _renderList,
  toDisplayString as _toDisplayString,
} from 'vue'

function render(_ctx, _cache) {
  return (_openBlock(), _createElementBlock('ul', null, [
    (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
      return (_openBlock(), _createElementBlock('li', {
        key: item.id,
      }, _toDisplayString(item.name), 1))
    }), 128))
  ]))
}

这里最关键的几个点是:

  • v-for 不会在运行时继续以“指令字符串”存在
  • 它会被改写成 renderList(...)
  • 每一项最终还是普通的 VNode 创建逻辑

所以它和 v-if 一样,也属于典型的结构性编译转换

renderList 到底做了什么

renderList 的职责不是“帮你神奇优化一切”,而是统一处理不同可遍历数据结构的渲染入口。

它可以处理:

  • 数组
  • 对象
  • 数字范围
  • 可迭代对象

它的本质更接近:

  • 遍历源数据
  • 对每项调用渲染回调
  • 返回一组待渲染的 VNode

真正的高效更新,不是只靠 renderList,而是后续还要结合:

  • key
  • diff 算法
  • block / patch 标记

Vue core 中的实现可参考:

https://github.com/vuejs/core/blob/a23fb59e83c8b65b27eaa21964c8baa217ab0573/packages/runtime-core/src/helpers/renderList.ts#L59

为什么 key 如此重要

很多人知道“v-for 要加 key”,但不知道它为什么重要。

核心原因是:Vue 需要知道“列表中的这个节点到底是不是上一次那个节点”。

有了稳定 key

  • 节点身份更明确
  • 重排时能更准确复用已有 DOM
  • 组件实例状态不容易错位

没有稳定 key

  • Vue 只能更保守地复用或重建节点
  • 列表重排时更容易出现状态串位

所以 key 不只是性能提示,更是身份标识

它和响应式系统的关系

items 变化时:

  1. 响应式系统通知组件更新
  2. 渲染函数重新执行
  3. renderList(_ctx.items, ...) 重新生成列表 VNode
  4. 运行时拿新旧列表做 diff
  5. 只更新真正变化的部分

所以你可以把它理解为:

  • 响应式系统负责“发现列表数据变了”
  • v-for 负责“定义怎样从数据生成节点”
  • diff 负责“尽量少动真实 DOM”

v-for 不只是数组

除了数组,v-for 也能遍历对象:

html
<li v-for="(value, key) in info" :key="key">
  {{ key }}: {{ value }}
</li>

也能遍历数字范围:

html
<span v-for="n in 5" :key="n">{{ n }}</span>

本质上都一样:
Vue 先把它统一翻译成“生成一组渲染项”的过程,再交给运行时继续处理。

性能真正该注意什么

v-for 的性能不在于“有没有循环”本身,而在于:

  • 列表量级多大
  • 是否提供稳定 key
  • 列表项内部是否有大量动态内容
  • 是否频繁重排

所以真正的优化重点通常是:

  1. 提供稳定、唯一的 key
  2. 避免无意义的大量重排
  3. 大列表时使用分页、虚拟列表等策略

一句话理解

v-for 不是模板里偷偷跑了一个“神秘循环”,而是 Vue 编译器先把循环表达式改写成列表渲染逻辑,再让运行时把这些结果高效映射到真实 DOM。

总结

特性说明
编译本质v-for 被改写为 renderList(...) 等列表渲染逻辑
主要机制结构性编译转换 + diff 更新
key 作用标识节点身份,帮助复用和稳定更新
响应式关系数据变化后重新生成列表 VNode
性能重点稳定 key、控制重排、处理大列表策略

Released under the CC BY-NC-SA 4.0 License.