Vue3 内置组件
1. KeepAlive (缓存组件)
KeepAlive 是面试中最常考的内置组件。它的核心作用是缓存不活动的组件实例,而不是销毁它们。
底层原理
- 抽象组件:
KeepAlive本身也是一个 Vue 组件,但它在setup或render中只返回它包裹的子组件(vnode),自身不会渲染任何额外的真实 DOM。 - 缓存字典:它内部维护了一个
cache对象(存储缓存的 vnode)和一个keys数组(存储缓存键)。 - 生命周期劫持:
- 当包裹的组件将要被卸载时,
KeepAlive会拦截这个操作。它不会去调用子组件的unmount,而是将其真实 DOM 从父节点中移除(移动到一个隐藏的离屏容器中),并将组件实例存入cache。 - 此时,子组件会触发
deactivated生命周期,而不是unmounted。 - 当组件再次被挂载时,
KeepAlive从cache中取出 vnode,直接将它的真实 DOM 重新插入回页面中,并触发activated生命周期。
- 当包裹的组件将要被卸载时,
面试高频:LRU 缓存淘汰算法
当传入了 max 属性时,KeepAlive 内部采用了 LRU (Least Recently Used,最近最少使用) 策略。
- 每次命中缓存时,将该组件的
key从keys数组中剔除,并重新push到数组尾部(标记为最新使用)。 - 当缓存数量超过
max时,直接删除keys数组的第一个元素(即最久未被使用的组件),并销毁对应的组件实例释放内存。
2. Teleport (传送门)
Teleport 是 Vue3 引入的新组件,主要用于解决嵌套极深的组件中(如弹窗 Modal、Tooltip)出现的 CSS z-index 失效或 overflow: hidden 裁剪问题。
底层原理
- 突破 DOM 层级限制:正常情况下,组件的真实 DOM 是按照组件树的层级结构生成的。
Teleport允许你通过to属性(如to="body"),将它包裹的内容在渲染阶段强制插入到指定的 DOM 节点下。 - 逻辑层级保持不变:虽然物理 DOM 结构被“传送”到了外面,但它的组件逻辑层级(虚拟 DOM 树)依然保持原样。这意味着它依然可以接收父组件的
props,依然可以正常触发emit事件,甚至依然受父级Provide/Inject的控制。 - 实现细节:在渲染管线(Render Pipeline)执行到
Teleport节点时,Vue 内部通过teleport.process方法接管挂载逻辑。它会将子节点挂载到target(目标节点)上,并在原位置留下一个注释节点(Comment Node)作为占位符。
3. Suspense (异步组件兜底)
Suspense 也是 Vue3 的新增特性,用于处理组件树中存在异步依赖的情况(如 defineAsyncComponent 或带有 async setup() 的组件)。
底层原理
- 状态机:
Suspense内部维护了两个插槽(Slot):#default(真实内容)和#fallback(兜底内容,如 Skeleton 骨架屏)。 - 依赖收集与等待:
- 当渲染
#default插槽时,如果遇到了异步组件,Vue 会收集这个异步 Promise。 - 在所有异步依赖 resolve 之前,
Suspense会立刻渲染#fallback插槽的内容。 - 当所有的 Promise 都变为 resolved 后,
Suspense会进行一次状态切换,卸载#fallback,并挂载真实的#default内容。
- 当渲染
- 优势:在 Vue2 中,如果父组件有 3 个子组件都需要发请求,我们需要在 3 个子组件内部各自维护一套
loading状态。有了Suspense,我们可以在最外层统筹这些异步状态,极大地简化了业务代码。
4. Transition (过渡动画)
Transition 为组件的进入和离开提供动画能力。
底层原理
- 事件钩子机制:它通过监听真实 DOM 的
transitionend或animationend事件来判断动画何时结束。 - DOM 挂载与卸载的延迟:
- 进入 (Enter):在挂载真实 DOM 前,先添加
v-enter-from和v-enter-active类名。DOM 挂载后,在下一帧移除v-enter-from,添加v-enter-to,触发浏览器 CSS 动画。 - 离开 (Leave):当触发
v-if="false"导致组件将要卸载时,Transition会拦截卸载操作,让 DOM 继续保留在页面上。然后添加离开相关的类名触发动画。等到监听到动画结束事件后,才真正执行 DOM 的卸载。
- 进入 (Enter):在挂载真实 DOM 前,先添加