Vue Fragment 锚点机制

一、核心理解:从"必须单根"到"允许多根"

背景变化:

  • Vue 2 的限制 :每个组件模板必须有一个单一的根元素包裹所有内容。
  • Vue 3 的突破 :组件可以直接返回多个平级的根节点,无需额外包装。

带来的新问题:

当组件返回多个平级节点(如 <div>A</div><p>B</p>)时,在底层渲染时需要一种方式来追踪和管理这一组节点------因为它们没有共同的父DOM元素作为"抓手"。

解决方案:

使用两个空文本节点作为锚点,像书签一样标记出这组节点的开始和结束位置。

简单来说:Vue 3 让你写模板时摆脱了单根限制,但底层实现上,为了能有效管理这些"散装"的节点,引入了锚点系统来标记它们的范围。

二、锚点的核心价值:在多Fragment场景下界定所有权

这是理解锚点必要性的最关键场景。假设一个容器内需要渲染多个组件,每个组件都是多根节点的Fragment:

xml 复制代码
<container>
  <!-- 如果没有锚点,所有节点混杂在一起 -->
  <div>A1</div>  <!-- 属于哪个Fragment? -->
  <p>B1</p>     <!-- 属于哪个Fragment? -->
  <div>A2</div>  <!-- 属于哪个Fragment? -->
  <p>B2</p>     <!-- 属于哪个Fragment? -->
</container>

注:当多个组件(每个都是多根节点)一起渲染时,所有子节点混在一起,无法区分哪些节点属于哪个组件。

问题:无法区分节点归属,无法单独更新或删除某个Fragment。

xml 复制代码
<container>
  <!-- 使用锚点后,范围清晰 -->
  [空文本 start1] <!-- Fragment 1 开始 -->
  <div>A1</div>   <!-- 明确属于 Fragment 1 -->
  <p>B1</p>       <!-- 明确属于 Fragment 1 -->
  [空文本 end1]   <!-- Fragment 1 结束 -->
  
  [空文本 start2] <!-- Fragment 2 开始 -->
  <div>A2</div>   <!-- 明确属于 Fragment 2 -->
  <p>B2</p>       <!-- 明确属于 Fragment 2 -->
  [空文本 end2]   <!-- Fragment 2 结束 -->
</container>

注:每个Fragment都有自己的一对锚点,锚点之间的节点就是这个Fragment的内容,这样就不会混淆了。

三、锚点的三大操作能力(持有引用即可)

只要保存了startAnchorendAnchor的引用,你就拥有了对这个Fragment内节点的完整控制权。

1. 精准遍历与查找

javascript 复制代码
// 遍历Fragment内所有子节点
// 从开始锚点的下一个兄弟节点开始,到结束锚点前结束
let node = startAnchor.nextSibling;  // 获取第一个实际子节点
while (node !== endAnchor) {         // 只要还没遇到结束锚点
    console.log('当前节点:', node);  // 处理这个节点
    node = node.nextSibling;         // 移动到下一个兄弟节点
}
// 注:这个循环能精准遍历Fragment内的所有子节点,不会跑到其他Fragment的节点
​
// 快速查找第N个子节点(例如找"a2")
function getFragmentChild(startAnchor, endAnchor, index) {
    let current = startAnchor.nextSibling;  // 从第一个子节点开始
    let count = 0;
    // 遍历直到找到第index个节点或遇到结束锚点
    while (current !== endAnchor && count < index) {
        current = current.nextSibling;  // 移动到下一个节点
        count++;                       // 计数
    }
    // 如果找到了就返回节点,否则返回null
    return current !== endAnchor ? current : null;
}
// 注:要找Fragment里的第2个子节点,就调用getFragmentChild(startAnchor, endAnchor, 1)
// (因为索引从0开始,第2个节点的索引是1)

2. 安全的范围操作

  • 在Fragment前插入新内容

    scss 复制代码
    // 在Fragment开始锚点前插入新节点
    container.insertBefore(newNode, startAnchor);
    // 注:这样插入的节点不会进入Fragment内部,而是在Fragment前面
  • 删除整个Fragment

    ini 复制代码
    // 先删除Fragment内的所有子节点
    let node = startAnchor.nextSibling;  // 从第一个实际子节点开始
    while (node !== endAnchor) {         // 直到遇到结束锚点
        const next = node.nextSibling;   // 先保存下一个节点
        container.removeChild(node);     // 删除当前节点
        node = next;                     // 移动到下一个节点
    }
    // 最后移除两个锚点节点本身
    container.removeChild(startAnchor);
    container.removeChild(endAnchor);
    // 注:这样就完全清除了这个Fragment及其所有内容

3. 独立的存在与更新

因为每个Fragment的边界由自己的锚点唯一确定,所以:

  • 可以独立挂载或卸载,不影响其他Fragment。

    注:因为每个Fragment有自己的锚点边界,所以删除一个Fragment时,不会误删其他Fragment的节点。

  • 更新时只需在自己的锚点范围内进行Diff,性能更高,更安全。

    注:Vue的虚拟DOM diff算法只需要在startAnchor和endAnchor之间的节点中进行,不会扫描整个容器,效率更高。

相关推荐
YHL5 分钟前
🎯 Danci —— 用 AI 驱动开发一个全栈英语单词学习平台
前端·后端
平头哥~6 分钟前
Day 20 _ 3D 翻卡_perspective 写错人,卡片就穿帮
前端·3d·css3·学习资料
小聪70814 分钟前
elpis-core 前端 Webpack 工程化实践
前端
2601_9539880715 分钟前
Ricon组态实时监控 - 毫秒级数据可视化
前端·物联网·数学建模·信息可视化·架构·前端框架
V1588972620139 分钟前
从滴滴模式看机器人租赁:撮合型租赁平台开发源码的调度系统设计思路
linux·前端·机器人
szarron1 小时前
国产手持式频谱分析仪选型攻略:TFN RC系列 vs HTOOL SA8T频谱分析仪 专业参数对比(军工/路测/调试全覆盖)
开发语言·前端·状态模式
开开心心就好1 小时前
免费桌签打印工具,支持批量导入名字
前端·javascript·人工智能·docker·jupyter·智能手机·语音识别
涛涛ing1 小时前
2026 年 9 月,整个 npm 生态的「心脏」都被 Rust 换了
前端
IT_陈寒1 小时前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端
CappuccinoRose1 小时前
Web Components 基础
前端·javascript·交互·web component·shadow dom