【前端】【vant】van-swipe的虚拟懒加载(懒挂载)机制

涉及到的业务逻辑

使用van-swipe显示1000道题的翻页滑动

vant的真实机制(虚拟懒挂载)是怎么回事?

van-swipe 在 Vant 3.x / 4.x 内部,采用了 lazy render + 复用 track 的实现方式。
它不会一次性把所有 的内容挂载到 DOM,而是动态控制 “挂载数量窗口”。
1.初次渲染

  • 默认只渲染当前页 + 前一页 + 后一页(共3个 item);
  • 其余 item 只是预留 <div class=“van-swipe-item”> 容器,但内容为空。
    2.滑动时
  • 当滑动到第 N 页时,组件会懒挂载第 N+1 页内容;
  • 同时可能卸载最早的页面(具体取决于内部缓存策略)。
    3.观察 DOM
  • 一开始只看到 3 个真实内容;
  • 不断滑动时,van-swipe 的虚拟列表动态增加;
  • 但不会一次性加载 100 页的内容到内存中。

问题1:如果滑动到第N页时,会懒挂载第N+1页内容,那是不是100个DOM同时存在?为什么不会造成卡顿呢?

每个 本身只是一个容器;
图片/视频等资源懒加载的仍然是懒加载;
Vue 会复用响应式对象引用,销毁的少;
现代浏览器对不可见节点有 GPU / JS 优化(不会持续绘制)。
换句话说:
看似 100 个节点都在,但 GPU 只渲染当前和邻近几屏。
所以仍然能保持顺滑。

问题2:为什么即使DOM节点增多,页面依旧顺滑

这背后其实涉及浏览器的渲染管线(Rendering Pipeline)、
Vue 的响应式对象复用机制、
以及 GPU/JS 层的“懒绘制”和“合成层优化”。

浏览器渲染管线的简化模型

浏览器渲染一帧(Frame)的主要步骤是:

  1. JavaScript 执行(更新 DOM / 样式属性)
  2. Style Calculation(计算样式)
  3. Layout / Reflow(计算元素位置)
  4. Paint(绘制像素)
  5. Composite(合成多个图层并送入 GPU 渲染)
不可见节点不会触发layout/Paint

只保留它们的几何信息(bounding box)在内存中
在van-swipe的实现中,只有当前页和相邻页的元素被设置了
transform: translateX(…),这些元素处于可见视口内,

不可见节点所在层被“裁剪”

Chrome / Safari / Edge 都使用 Compositing Layers(合成层)。
当元素通过 transform, will-change, 或动画被 GPU 加速时,
浏览器会把这个元素单独放在一个合成层(Texture)中。

van-swipe 整个容器就是一个单独的 layer。
它的子项 van-swipe-item会被当作该 layer 的子帧,只渲染当前 offset 对应的部分。

javaScript层面的开销几乎为零

vue的响应式系统有“引用复用机制”
在初始化时,quesitonList是一个大数组,每个元素是一个对象
当我们滑动时:
v-for=“opt in questions[index].opts”
只会访问当前索引对应的引用,vue不会再每次滑动时新建100个响应式对象
每次改变的都是index,如果key不变,则不做改变,如果key改变,则旧的DOM是不变的,新的DOM会复用旧的DOM并改变其中的值
例如:题目DOM如下,

<div class="question-box">
  <h3>第 8 题</h3>
  <p>这是第 8 题的内容</p>
  <label><input type="radio">A</label>
  <label><input type="radio">B</label>
</div>

当滑动到第9题时
发现旧 VNode 的根节点类型相同;
直接修改 h3的 textContent;
修改 p的textContent;
重绘被选中的 input状态;
其他 DOM 元素保持原位不动。

问题3:既然vue是复用,那为何在浏览器元素树中能看到100道题的DOM节点?

这涉及到了vue-vant-浏览器三个层面

你的数据

Vue(模板编译、虚拟DOM diff)

Vant(组合 Vue 子组件、封装交互逻辑)

浏览器(渲染真实 DOM、绘制到屏幕)


vue层面
vue把v-for解析成虚拟DOM节点数组,然后diff+patch到真实DOM

<van-swipe>
  <van-swipe-item v-for="q in questions" :key="q.id">
    <div class="question">{{ q.title }}</div>
  </van-swipe-item>
</van-swipe>

vant层面
vant是vue组件,有自己的模板和动画逻辑

<div class="van-swipe__track" style="transform: translateX(-offset)">
  <slot></slot>
</div>

浏览器层

<div class="van-swipe">
  <div class="van-swipe__track" style="transform: translateX(-300px)">
    <div class="van-swipe-item">题1</div>
    <div class="van-swipe-item">题2</div>
    ...
  </div>
</div>

问题4:帧是什么?

  • 在浏览器中,一帧(Frame)就是:
    浏览器从执行 JavaScript → 计算布局 → 绘制像素,
    再把这一画面显示到屏幕上的一个「画面周期」。

  • 一帧 = 屏幕上显示的一张静态“图片”

  • 动画(比如滑动、滚动、渐变)= 多帧连续显示

  • 常见刷新率为 60Hz → 即 1 秒 60 帧。
    1000ms ÷ 60 ≈ 16.67ms / 帧
    浏览器必须在16.67ms中完成js执行,样式计算,布局,绘制,合成,不然就会掉帧

  • Chrome DevTools → Performance → Record
    里面有每一帧的时间分配图(JS、Layout、Paint、Composite)。
    或者用 FPS Meter 工具,在滑动时看帧率变化。

  • JS 执行太重(for 循环、复杂计算);DOM 更新太多(layout 大量触发);GPU 合成层太多(paint 时间太长);都会让帧率从 60fps 掉到 40fps、30fps 等。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值