文章目录
- 前言
- [一、key 是什么](#一、key 是什么)
-
- [1.1 定义](#1.1 定义)
- [1.2 Diff 中的匹配规则](#1.2 Diff 中的匹配规则)
- [二、v-for 中的 key](#二、v-for 中的 key)
-
- [2.1 正确用法](#2.1 正确用法)
- [2.2 Vue 3 要求 key](#2.2 Vue 3 要求 key)
- [2.3 无 key 时的行为](#2.3 无 key 时的行为)
- [三、index 作 key 的问题](#三、index 作 key 的问题)
-
- [3.1 头部插入示例](#3.1 头部插入示例)
- [3.2 使用 id 作 key](#3.2 使用 id 作 key)
- [3.3 index 何时「看起来没问题」](#3.3 index 何时「看起来没问题」)
- [四、key 不稳定的问题](#四、key 不稳定的问题)
-
- [4.1 Math.random()](#4.1 Math.random())
- [4.2 不稳定 key 的后果](#4.2 不稳定 key 的后果)
- [4.3 key 的要求](#4.3 key 的要求)
- 五、强制重新渲染组件
-
- [5.1 原理](#5.1 原理)
- [5.2 典型场景](#5.2 典型场景)
- [5.3 与 watch 对比](#5.3 与 watch 对比)
- [六、key 的其他用法](#六、key 的其他用法)
-
- [6.1 动态组件](#6.1 动态组件)
- [6.2 过渡动画](#6.2 过渡动画)
- [6.3 条件渲染切换](#6.3 条件渲染切换)
- 七、面试聚焦
-
- [7.1 key 不稳定导致状态错乱](#7.1 key 不稳定导致状态错乱)
- [7.2 为什么 Math.random() 不能做 key?](#7.2 为什么 Math.random() 不能做 key?)
- [7.3 key 的作用仅是性能吗?](#7.3 key 的作用仅是性能吗?)
- 八、易混淆点
- 九、思考与练习
- 总结
前言
key 是 Vue 列表渲染和组件切换中的重要属性,帮助 Diff 算法识别节点身份,决定 DOM 复用还是重建。本篇会讲清楚:
- key 在 Diff 中的作用
- index 作 key 的问题
- 强制重新渲染组件
- key 选型与常见陷阱
一、key 是什么
1.1 定义
key 是 Vue 为 VNode 提供的唯一标识,用于在新旧节点比较时判断「是不是同一个节点」。
vue
<ul>
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
</ul>
1.2 Diff 中的匹配规则
相同 type + 相同 key → 认为是同一节点 → patch(复用 DOM,更新差异)
相同 type + 不同 key → 不同节点 → 销毁旧的,创建新的
不同 type → 直接替换
key 让 Diff 在列表乱序、增删时仍能正确对应节点,而不是仅按索引顺序硬比。
二、v-for 中的 key
2.1 正确用法
vue
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
使用稳定且唯一的业务 ID(数据库 id、uuid 等),列表增删改时节点身份不变。
2.2 Vue 3 要求 key
Vue 3 的 v-for 必须提供 key,否则开发环境会警告。Vue 2 中 key 可选,但生产环境仍强烈建议加上。
2.3 无 key 时的行为
未提供 key 时,Vue 按索引顺序依次 patch:新列表第 i 项与旧列表第 i 项比较。列表中间插入/删除时,容易造成错误复用。
三、index 作 key 的问题
3.1 头部插入示例
vue
<!-- ❌ 使用 index -->
<li v-for="(item, index) in list" :key="index">
<input v-model="item.name" />
{{ item.name }}
</li>
初始: [A, B, C] key=0,1,2
头部插入 D: [D, A, B, C]
Diff 按 index 匹配:
key=0: 旧 A → 新 D (复用 A 的 DOM,内容变成 D,输入框状态错乱)
key=1: 旧 B → 新 A
key=2: 旧 C → 新 B
key=3: 新增 C
结果:DOM 复用错误,输入框内容错位,组件内部状态混乱。
3.2 使用 id 作 key
vue
<!-- ✅ 使用唯一 id -->
<li v-for="item in list" :key="item.id">
<input v-model="item.name" />
</li>
插入 D 后:
id=d → 新建 D 的 DOM
id=a → patch A(正确复用)
id=b → patch B
id=c → patch C
只有新增项创建 DOM,其余正确复用。
3.3 index 何时「看起来没问题」
| 场景 | index 作 key |
|---|---|
| 纯展示、无状态 | 可能可用 |
| 只有尾部追加 | 通常正常 |
| 有插入/删除/排序 | 会出问题 |
| 含输入框、checkbox 等 | 会出问题 |
结论:无唯一 id 时,应用组合字段或生成稳定 id,不要图省事用 index。
四、key 不稳定的问题
4.1 Math.random()
vue
<!-- ❌ 每次 render 新 key -->
<div v-for="item in list" :key="Math.random()">
每次渲染 key 都变 → Diff 认为全是新节点 → 销毁并重建所有 DOM,性能差且状态全丢。
4.2 不稳定 key 的后果
- 不必要的 DOM 创建/销毁
- 组件 state 丢失(输入内容、滚动位置等)
- 过渡动画异常
- 子组件生命周期反复 mounted/unmounted
4.3 key 的要求
| 要求 | 说明 |
|---|---|
| 唯一 | 同一列表内不重复 |
| 稳定 | 同一数据项多次 render 间 key 不变 |
| 可预测 | 不用 random、Date.now() 等每次变化的值 |
五、强制重新渲染组件
5.1 原理
同一组件类型,key 变化时 Vue 视为不同实例:销毁旧组件,创建新组件。
vue
<UserProfile :user-id="userId" :key="userId" />
userId 从 1 变为 2 时,组件完全重建,内部 state 重置,相当于「换了一个组件」。
5.2 典型场景
vue
<!-- 切换用户时重置表单 -->
<EditForm :key="currentUserId" :user-id="currentUserId" />
<!-- 路由同组件不同参数,强制刷新 -->
<router-view :key="$route.fullPath" />
5.3 与 watch 对比
| 方式 | 行为 |
|---|---|
| 改 key | 销毁 + 重建,state 全清 |
| watch route/ props | 复用实例,手动重置数据 |
需要「完全重来」用 key;需要「保留部分状态」用 watch 更新。
六、key 的其他用法
6.1 动态组件
vue
<component :is="currentTab" :key="currentTab" />
切换 Tab 时 key 变化,确保不同面板是独立实例(配合 KeepAlive 时另说)。
6.2 过渡动画
vue
<Transition name="fade" mode="out-in">
<component :is="view" :key="view" />
</Transition>
key 变化触发 leave + enter 过渡;无 key 时同组件可能不触发动画。
6.3 条件渲染切换
vue
<Transition>
<div v-if="type === 'A'" key="A">A 内容</div>
<div v-else key="B">B 内容</div>
</Transition>
A/B 切换时 key 不同,正确触发过渡。
七、面试聚焦
7.1 key 不稳定导致状态错乱
index 在增删时变化、random 每次变,导致 Diff 错误复用 DOM,输入框等内容错位。
7.2 为什么 Math.random() 不能做 key?
每次 render 生成新 key,所有节点被视为新节点,全部重建,无复用、无性能、状态丢失。
7.3 key 的作用仅是性能吗?
不是 。更重要的是正确性:保证节点身份识别准确,DOM 和组件状态正确复用。
八、易混淆点
- key 给 VNode,不是给 DOM attribute:渲染到 HTML 的 key 属性是另一回事(Vue 3 一般不输出)。
- index 纯追加列表可能正常:一旦有插入删除就会踩坑。
- 强制刷新用 key :改 key 比
v-if切换更彻底重置组件。 - Vue 3 v-for 必须有 key:遗漏会警告。
- key 唯一指同级列表:不同 v-for 块之间 key 可重复(但不推荐故意重复)。
九、思考与练习
1. key 在 Diff 中的作用?
解析:标识节点身份;相同 type + key 则 patch 复用,不同则销毁重建。
2. index 作 key 何时出问题?
解析:列表有插入、删除、排序,或项内有输入状态时;索引重排导致错误复用。
3. 如何强制组件重新渲染?
解析:改变组件上的 :key 为新的稳定值,Vue 销毁旧实例并创建新实例。
4. 没有业务 id 怎么办?
解析:组合多个字段生成稳定 key,或后端补 id;避免 index 和 random。
5. key 与性能的关系?
解析:稳定 key 减少不必要的 DOM 操作;但首要目的是保证复用正确性,不仅是性能优化。
总结
- key:VNode 唯一标识,Diff 匹配节点身份
- 规则:同级唯一、稳定、可预测
- 推荐:业务 id;避免 index(有增删时)、Math.random()
- 副作用:改 key 可强制销毁重建组件
- 场景:v-for、动态组件、Transition、路由刷新