Skip to main content

Vue3 内置组件

1. KeepAlive (缓存组件)

KeepAlive 是面试中最常考的内置组件。它的核心作用是缓存不活动的组件实例,而不是销毁它们

底层原理

  • 抽象组件KeepAlive 本身也是一个 Vue 组件,但它在 setuprender 中只返回它包裹的子组件(vnode),自身不会渲染任何额外的真实 DOM
  • 缓存字典:它内部维护了一个 cache 对象(存储缓存的 vnode)和一个 keys 数组(存储缓存键)。
  • 生命周期劫持
    • 当包裹的组件将要被卸载时,KeepAlive 会拦截这个操作。它不会去调用子组件的 unmount,而是将其真实 DOM 从父节点中移除(移动到一个隐藏的离屏容器中),并将组件实例存入 cache
    • 此时,子组件会触发 deactivated 生命周期,而不是 unmounted
    • 当组件再次被挂载时,KeepAlivecache 中取出 vnode,直接将它的真实 DOM 重新插入回页面中,并触发 activated 生命周期。

面试高频:LRU 缓存淘汰算法

当传入了 max 属性时,KeepAlive 内部采用了 LRU (Least Recently Used,最近最少使用) 策略。

  • 每次命中缓存时,将该组件的 keykeys 数组中剔除,并重新 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 的 transitionendanimationend 事件来判断动画何时结束。
  • DOM 挂载与卸载的延迟
    • 进入 (Enter):在挂载真实 DOM 前,先添加 v-enter-fromv-enter-active 类名。DOM 挂载后,在下一帧移除 v-enter-from,添加 v-enter-to,触发浏览器 CSS 动画。
    • 离开 (Leave):当触发 v-if="false" 导致组件将要卸载时,Transition拦截卸载操作,让 DOM 继续保留在页面上。然后添加离开相关的类名触发动画。等到监听到动画结束事件后,才真正执行 DOM 的卸载。