前端八股之Vue

Web前端八股文Vue
2025-03-13 - 15:17
四季柚子
2025-03-13 - 15:17

Vue基础

1. Vue的基本原理

当一个Vue实例创建时,Vue会遍历属性,用proxy将它们转为getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

2. 双向数据绑定的原理

Vue3 的双向绑定原理基于ProxyReflect实现,相较于Vue2的Object.defineProperty,Proxy提供了更强大的功能和更好的性能。以下是Vue3双向绑定的基本原理和步骤:

  1. 数据劫持(Reactive)
    Vue3 使用Proxy对数据对象进行劫持,监听对象的读取和修改操作。当数据发生变化时,Proxy会自动触发相应的回调函数,通知视图更新。
    ProxyProxy可以拦截对象的读取(get)和修改(set)操作,无需像Object.defineProperty那样递归遍历对象的每个属性。 • ReflectReflect用于执行默认行为,如获取或设置属性值,确保Proxy的拦截操作不会影响正常逻辑。
  2. 依赖收集(Track)
    当组件渲染时,Vue3会通过Proxyget操作收集当前属性的依赖(即哪些组件或视图依赖于该属性)。这些依赖会被存储在一个全局的依赖映射表(targetMap)中,以便在数据变化时快速找到需要更新的视图。
    依赖映射表targetMap是一个WeakMap,键为被劫持的对象,值为另一个Map,存储该对象的所有属性和对应的依赖集合。
  3. 触发更新(Trigger)
    当数据被修改时,Proxyset操作会触发更新机制。Vue3会从targetMap中找到该属性的所有依赖,并通知它们执行更新操作。
    批量更新:为了提高性能,Vue3会将多个更新操作合并到一个微任务队列中,避免频繁的视图更新。
  4. 模板编译与视图更新
    Vue3 的模板编译器会将模板中的指令(如 v-model)解析为对应的渲染函数。当数据变化时,渲染函数会重新执行,生成新的虚拟DOM,并通过Diff算法高效地更新真实DOM。

3. computedwatch的区别

对于computed

  • 它支持缓存,只有依赖的数据发生了变化,才会重新计算
  • 不支持异步,当computed中有异步操作时,无法监听数据的变化
  • computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed

对于watch

  • 它不支持缓存,数据变化时,它就会触发相应的操作
  • 支持异步监听
  • 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值

当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用watch

总结

当需要进行数值计算,并且依赖于其它数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时都要重新计算。

当需要在数据变化时执行异步或开销较大的操作时,应该使用watch,使用watch选项允许执行异步操作 (访问一个API),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

4. slot是什么?有什么作用?

slot又名插槽,是Vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。slot又分三类,默认插槽,具名插槽和作用域插槽。

  • 默认插槽:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。

5. 常见的事件修饰符及其作用

  • ​.stop​:等同于JavaScript中的​event.stopPropagation()​,防止事件冒泡
  • .prevent​:等同于JavaScript中的​event.preventDefault()​,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播)
  • ​.capture:与事件冒泡的方向相反,事件捕获由外到内;
  • ​.self​:只会触发自己范围内的事件,不包含子元素;
  • .once​:只会触发一次。

6. v-ifv-show的区别

  • 手段:v-if是动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显隐;
  • 编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;
  • 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下,无论首次条件是否为真,都被编译,然后被缓存,而且DOM元素保留;
  • 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
  • 使用场景:v-if适合条件不大可能改变;v-show适合频繁切换。

7. v-model是如何实现的,语法糖实际是什么?

作用在表单元素上

动态绑定了input的value指向了messgae变量,并且在触发input事件的时候去动态把message设置为目标值

<input v-model="sth">
//  等同于
<input :value="message" @input="message=$event.target.value">

作用在组件上

在自定义组件中,v-model默认会利用名为value的prop和名为input的事件

本质是一个父子组件通信的语法糖,通过prop和emit实现。因此父组件v-model语法糖本质上可以修改为

<child :value="message"  @input="e => message = e"></child>

8. 对keep-alive的理解,它是如何实现的,具体缓存的是什么?

keep-alive 是Vue中一个非常重要的内置组件,用于实现组件的缓存功能。它的主要作用是保留组件的状态或避免重复渲染,从而提升性能。

1. keep-alive 的作用

keep-alive 是一个抽象组件,它不会在DOM中生成任何额外的节点,而是通过控制其包裹的动态组件(如<component>或路由组件)来实现缓存功能。具体来说:

  • 保留组件状态:当组件被切换时,keep-alive会将其从DOM中移除,但不会销毁该组件实例,而是将其缓存起来。当下次重新激活该组件时,直接复用缓存的实例,而不是重新创建。
  • 避免重复渲染:由于组件实例被缓存,因此可以避免不必要的生命周期钩子(如createdmounted)被触发,减少性能开销。

2. keep-alive 的实现原理

keep-alive的核心实现依赖于Vu 的组件生命周期和内部机制,主要包括以下几个方面:

(1)组件缓存机制

  • 当一个组件被keep-alive包裹时,Vue会在组件切换时检查该组件是否已经被缓存。
  • 如果组件已经被缓存,则直接复用缓存中的组件实例,而不会触发createdmounted等生命周期钩子。
  • 如果组件未被缓存,则会正常创建并初始化组件实例,并将其存储到缓存中。

(2)缓存管理

  • keep-alive 内部维护了一个缓存对象(通常是一个Map或类似结构),用于存储缓存的组件实例。
  • 每个组件实例通过其唯一的key(默认为组件的name属性,或者通过include/exclude指定的规则)进行标识。
  • 当组件被切换时,keep-alive会根据当前组件的`key 判断是否需要缓存或复用。

(3)生命周期钩子的特殊处理

  • keep-alive 缓存的组件会触发两个特殊的生命周期钩子:
    • activated:当组件被激活(从缓存中恢复)时触发。
    • deactivated:当组件被停用(被缓存但未显示)时触发。
  • 这些钩子允许开发者在组件缓存前后执行特定逻辑。

(4)缓存限制

  • keep-alive提供了max属性,用于限制缓存组件的数量。当缓存数量超过max时,最早缓存的组件会被移除(遵循 LRU 算法)。
  • 可以通过includeexclude属性指定哪些组件需要被缓存或排除。

3. keep-alive 具体缓存的是什么?

keep-alive缓存的核心内容是组件实例(VNode 和相关状态)。具体包括以下内容:

(1)组件实例

  • 组件实例的所有数据、方法、计算属性、监听器等都会被完整保存。
  • 例如,表单中的输入值、滚动位置、定时器等状态都会被保留。

(2)DOM 节点

  • 被缓存的组件对应的 DOM 节点也会被保留在内存中,而不是从 DOM 树中移除。
  • 这意味着组件的样式、布局等都不会丢失。

(3)生命周期状态

  • 组件的生命周期状态会被暂停(即不会触发unmounted钩子),直到组件再次被激活。

4. 使用场景

keep-alive适用于以下场景:

  • 频繁切换的组件:例如标签页(Tabs)、弹窗(Modal)等。
  • 需要保留状态的组件:例如表单页面、列表页面(分页、滚动位置等)。
  • 性能优化:避免重复渲染复杂的组件。

5. 注意事项

  • 内存占用:由于keep-alive会将组件实例保留在内存中,因此如果缓存的组件过多,可能会导致内存占用过高。
  • 清理工作:在deactivated钩子中,可能需要手动清理一些资源(如定时器、事件监听器等),以避免潜在的内存泄漏。
  • 动态组件keep-alive通常与动态组件(<component>)或路由组件配合使用。

9. nextTick的原理和作用

1. nextTick 的作用

nextTick是Vue提供的一个工具方法,用于在DOM更新完成后执行回调函数。它的主要作用是:

  • 确保DOM已更新:当我们在Vue中修改响应式数据后,Vue并不会立即更新DOM,而是将更新操作放入一个异步队列中。nextTick 可以让我们在DOM更新完成后执行某些逻辑。
  • 解决异步更新问题:由于Vue的响应式系统是基于异步更新机制的,直接在数据修改后访问DOM可能无法获取到最新的状态,而nextTick提供了一个可靠的时机。

示例代码:

import { nextTick } from 'vue';

const count = ref(0);

count.value++;
console.log(document.querySelector('#app').textContent); // 仍然是旧值

nextTick(() => {
  console.log(document.querySelector('#app').textContent); // 新值已更新
});

2. nextTick 的原理 Vue的nextTick实现依赖于JavaScript的事件循环机制和微任务/宏任务调度。以下是其核心原理:

(1)Vue 的异步更新机制

  • Vue在检测到数据变化时,并不会立即更新DOM,而是将更新操作放入一个队列中。
  • 这种设计是为了避免频繁的DOM操作(例如多次修改同一个数据时,只触发一次DO 更新),从而提升性能。

(2)微任务与宏任务

  • Vue会利用浏览器的微任务或宏任务来调度 DOM 更新。
  • 在Vue 3中,优先使用微任务,因为微任务的优先级更高,能够更快地执行。

(3)nextTick 的实现

  • 当调用nextTick时,Vue会将传入的回调函数注册到一个微任务队列中。
  • 当Vue完成当前的DOM更新后,微任务队列中的回调函数会被依次执行。

3. nextTick的应用场景

在实际开发中,nextTick常用于以下场景:

  1. DOM 操作
    • 当需要在数据更新后立即操作DO 时,可以使用nextTick确保DOM已更新。
    count.value++;
    nextTick(() => {
      console.log(document.querySelector('#app').textContent); // 获取最新 DOM 内容
    });
    
  2. 第三方库集成
    • 当需要在 DOM 更新后初始化第三方库(如图表库、动画库等)时,可以使用nextTick
  3. 调试和日志记录
    • 在调试过程中,可以通过nextTick确保数据和DOM状态一致后再打印日志。
  4. 动态样式调整
    • 如果需要根据DOM的实际尺寸或其他属性动态调整样式,可以在nextTick中完成。

4. Vue 3 的改进

相比Vue 2,Vue 3的 nextTick`在实现上更加简洁高效:

  • 基于 Composition API:Vue 3 的 nextTick 是一个独立的函数,可以直接从vue包中导入,无需依赖 Vue 实例。

5. 总结

nextTick是 Vue 中非常重要的一个工具,它解决了异步更新机制带来的 DOM 同步问题。通过理解其背后的原理(微任务/宏任务调度)和应用场景,我们可以在开发中更好地利用它来处理复杂的 DOM 操作和状态同步问题。

10. Vue template到render的过程

Vue 模板编译过程(Vue 3)

模板编译的核心流程为:template → AST → Render 函数
在Vue 3中,编译过程主要由@vue/compiler-dom包处理,流程如下:

1. 解析模板生成 AST(Abstract Syntax Tree)

将模板字符串解析为抽象语法树(AST),描述模板的结构和内容。

2. AST 转换(Transform)

  • 目标:对 AST 进行优化和转换,注入Vue特有的逻辑(如指令、事件、条件渲染)。
  • 关键步骤
    • 静态节点标记:标记静态节点(内容不会变化的节点),后续渲染时跳过更新。
    • 指令处理:将Vue指令(如 v-ifv-forv-bind)转换为渲染逻辑。
    • 事件处理:将@click等事件转换为 on 对象的渲染逻辑。
    • 插槽处理:解析插槽(<slot>)并生成对应的渲染逻辑。
    • 优化标记:通过patchFlag标记节点的更新类型(如动态属性、动态文本),帮助运行时快速判断是否需要更新。

3. 代码生成(Generate)

  • 目标:将AST转换为渲染函数(render function)的字符串形式。
  • 实现细节
    • 生成渲染代码:遍历AST,生成以hcreateVNode)为核心的渲染函数字符串。
    • 静态树提升:将静态节点(标记为静态的 AST 节点)提升到渲染函数外部,避免重复创建。
    • 代码优化:合并静态文本、优化动态表达式,减少运行时计算量。

核心优化点(Vue 3 新增)

  1. 静态树提升:静态节点直接缓存为静态片段(__staticTrees),避免每次渲染重复创建。
  2. Patch Flags:通过标记节点的更新类型(如patchFlag: 2表示动态属性),让运行时直接跳过无需更新的节点。
  3. 指令与转换分离:将指令逻辑抽象为可复用的转换器(Transformer),提升扩展性。
  4. 编译时优化:提前分析模板结构,减少运行时计算量(如合并静态文本、优化动态表达式)。

11. 描述下Vue自定义指令

1. 基本概念

Vue自定义指令是一种用于直接操作DOM的底层功能,通过封装可复用的DOM行为,实现与组件逻辑解耦的交互(如表单自动聚焦、图片懒加载、按钮权限控制等)。

2. 使用场景

DOM底层操作:如聚焦输入框、拖拽元素
全局行为封装:如权限控制指令v-permission
性能优化:如防抖/节流指令v-throttle
第三方库集成:如集成动画库或图表库

3. 创建方式

分为全局指令局部指令

// 全局指令
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})
// 局部指令(组件内)
const MyComponent = {
  directives: {
    focus: {
      mounted(el) {
        el.focus()
      }
    }
  }
}

4. 指令生命周期钩子

Vue3 指令生命周期与组件生命周期对齐,包含以下钩子:
created:元素属性初始化前调用(无 DOM 访问权限)
beforeMount:元素插入 DOM 前调用
mounted:元素插入 DOM 后调用
beforeUpdate:元素更新前调用
updated:元素更新后调用
beforeUnmount:元素卸载前调用
unmounted:元素卸载后调用

5. 钩子函数参数

每个钩子接收以下参数:
el:指令绑定的DOM元素
binding:包含valueoldValueargmodifiers 等属性 • **vnode**:当前虚拟节点 • **prevNode**:上一个虚拟节点(仅在beforeUpdateupdated`中可用)

6. 常见案例

案例 1:输入框自动聚焦

app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

案例 2:按钮权限控制

app.directive('permission', {
  mounted(el, binding) {
    const { value: role } = binding
    const userRole = 'admin'
    if (role !== userRole) el.style.display = 'none'
  }
})

7. 原理简析

• Vue自定义指令通过directiveAPI 注册,在模板编译阶段被解析为渲染函数中的指令对象。
• 底层通过withDirectives函数将指令逻辑注入到虚拟节点的props中,在DOM挂载/更新时触发对应钩子。

12. Vue的性能优化有哪些

编码阶段

  • v-ifv-for不能连用
  • 如果需要使用v-for给每项元素绑定事件时使用事件代理
  • SPA页面采用keep-alive缓存组件
  • 在更多的情况下,使用v-if替代v-show
  • key保证唯一
  • 使用路由懒加载、异步组件
  • 防抖、节流
  • 第三方模块按需导入
  • 长列表滚动到可视区域动态加载
  • 图片懒加载

SEO优化

  • 预渲染
  • 服务端渲染SSR

打包优化

  • 压缩代码
  • Tree Shaking/Scope Hoisting
  • 使用cdn加载第三方模块
  • 抽离公共文件
  • sourceMap优化

用户体验

  • 骨架屏
  • PWA
  • 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

13. 对SPA单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在Web页面初始化时加载相应的HTML、JavaScript和CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。

优点

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染
  • 基于上面一点,SPA 相对对服务器压力小
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理

缺点

  • 初次加载耗时多:为实现单页Web应用功能及显示效果,需要在加载页面的时候将JavaScript、CS 统一加载,部分页面按需加载
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理
  • SEO难度较大:由于所有的内容都在一个页面中动态替换显示,所以在SEO上其有着天然的弱势

组件通信

感觉我很熟啊,不需要写了

算了,稍微写一下吧

父子:

  • props
  • emit
  • ref和expose
  • 3.4新增的defineModel 兄弟:
  • provide/inject
  • 父组件中转 祖孙:
  • provide/inject
  • props链式 论外:
  • 全局状态管理库,如pinia
  • 路由路径参数或者查询参数
  • 本地存储

路由

1. Vue-Router的懒加载如何实现

非懒加载:

import List from '@/components/list.vue'
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

懒加载:

const List = () => import('@/components/list.vue')
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

2. 路由的hash和history模式的区别

hash模式

简介: hash模式是开发中默认的模式,它的URL带着一个#,例如:http://www.abc.com/#/vue,它的hash值就是#/vue。

特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。

原理:hash模式的主要原理就是onhashchange()事件

使用onhashchange()事件的好处就是,在页面的hash值发生变化时,无需向后端发起请求,window就可以监听事件的改变,并按规则加载相应的代码。除此之外,hash值变化对应的URL都会被浏览器记录下来,这样浏览器就能实现页面的前进和后退。虽然是没有请求后端服务器,但是页面的hash值和对应的URL关联起来了。

history模式

简介: history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。

特点: 当使用history模式时,URL就像这样:http://abc.com/user/id。相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。

API: history api可以分为两大部分,切换历史状态和修改历史状态:

  • 修改历史状态:包括了HTML5 History Interface中新增的​pushState()​和​replaceState()​方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。
  • 切换历史状态:包括​forward()​、​back()​、​go()​三个方法,对应浏览器的前进,后退,跳转操作。

3. routerouter的区别

route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数

router是“路由实例”对象包括了路由的跳转方法,钩子函数等。

4. Vue-router路由钩子在生命周期的体现

Vue-Router导航守卫

有的时候,需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。

为此有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,或者组件级的

全局路由钩子

vue-router全局有三个路由钩子;

  • router.beforeEach全局前置守卫 进入路由之前
  • router.beforeResolve全局解析守卫(2.5.0+)在beforeRouteEnter调用之后调用
  • router.afterEach全局后置钩子 进入路由之后

单个路由独享钩子

beforeEnter

如果不想全局配置守卫的话,可以为某些路由单独配置守卫,有三个参数∶ to、from、next

{      
    path: '/',      
    name: 'login',      
    component: login,      
    beforeEnter: (to, from, next) => {        
        console.log('即将进入登录页面')        
        next()      
    }  
}

虚拟DOM

1. Vue中key的作用

一、在 v-if 中使用 key:强制重新渲染

1. 问题背景

当使用 v-if 切换相同类型的元素时,Vue默认会复用已有元素(出于性能考虑)。这种复用可能导致以下问题:

<template>
  <!-- 切换登录/注册表单 -->
  <form v-if="isLogin">
    <input type="text" placeholder="用户名">
  </form>
  <form v-else>
    <input type="text" placeholder="邮箱">
  </form>
</template>
  • 现象:从“登录”切换到“注册”时,输入框的placeholder会更新,但用户已输入的内容不会清空
  • 原因:Vue 复用了<input>元素,仅更新其属性,未重新渲染 DOM 节点。

2. 解决方案:添加 key

<template>
  <form v-if="isLogin" key="login-form">
    <input type="text" placeholder="用户名">
  </form>
  <form v-else key="register-form">
    <input type="text" placeholder="邮箱">
  </form>
</template>
  • 效果key的唯一性会强制Vu 销毁旧元素并创建新元素,彻底清除残留状态。
  • 本质:破坏了Vue的“就地复用”策略,确保元素独立。

二、在v-for中使用key:高效更新列表

1. 问题背景

当使用v-for渲染动态列表时,Vue默认采用“就地更新”策略:

<template>
  <div v-for="item in list">{{ item.text }}</div>
</template>
  • 现象:如果列表数据顺序变化(如排序、过滤),Vue会直接复用现有DOM元素,仅更新内容
  • 潜在问题
    • 状态错乱:若列表项包含组件或表单元素,复用可能导致内部状态未正确更新。
    • 性能浪费:无法通过移动DOM优化渲染,可能触发不必要的子组件重新渲染。

2. 解决方案:添加key

<template>
  <div v-for="item in list" :key="item.id">{{ item.text }}</div>
</template>
  • 作用key作为唯一标识符,帮助Vue跟踪每个节点的身份。
  • 优化原理
    • 精准复用:当数据顺序变化时,Vue通过key识别节点,移动DOM元素而非重新创建。
    • 避免状态错乱:组件或表单元素与正确的数据项保持绑定。

3. 错误用法:用索引作为key

<template>
  <!-- 不推荐! -->
  <div v-for="(item, index) in list" :key="index"></div>
</template>
  • 问题:当列表发生插入、删除或排序时,索引无法稳定对应数据项,导致:
    • 组件状态混乱(如复选框选中错误项)。
    • 子组件生命周期钩子被错误触发。
  • 正确做法:使用数据中唯一的标识(如id)。

三、深入原理:key如何影响Diff算法

Vue 的虚拟 DOM Diff 算法通过key和标签类型判断节点是否可复用:

1. sameVNode函数

在对比新旧节点时,Vue 会调用sameVNode判断是否复用:

function sameVNode(a, b) {
  return (
    a.key === b.key && // key 相同
    a.tag === b.tag    // 标签类型相同
    // ...其他条件(如命名空间等)
  )
}
  • key相同且标签一致,则复用DOM元素,仅更新属性。
  • key不同,则销毁旧元素,创建新元素。

2. Key 对性能的影响

  • 无 Key:Vue 会按顺序遍历新旧子节点,逐个对比(时间复杂度 O(n))。
  • 有 Key:生成{ key: node }的映射表,直接通过key查找节点(时间复杂度O(1)),大幅减少比对次数。

四、总结:key 的最佳实践

场景 作用 推荐值 注意事项
v-if 切换 强制重新渲染 唯一静态字符串 避免与其他元素 key 冲突
v-for 列表 精准追踪数据项 数据中的唯一标识(如 id 不要用索引或随机值
  • 核心规则key 应在同一父级下唯一且稳定,与数据身份强关联。
  • 特殊场景:在<transition><keep-alive>中,key 也可用于强制重新渲染组件。
话题状态:正常
20 × 2025-03-14 - 17:18