Skip to content

响应式更新

演示两种改数据的方式,切换「更新方式」即可对比:

  • 换新数组(推荐):换成新数组后 setList(list)。组件通过 updateItem 把最新数据 交给可视区内的每一项,行上的轮次跟着涨。
  • 原地修改:直接改字段、也不调 setList。组件完全不知道数据动过,行上的轮次会停住, 点 refreshItems() 才追上。

它是怎么知道数据变了的

它不知道——纯 DOM 用法下没有任何侦测机制,是你调 setList 告诉它的。

框架包(vue / react 等)里这一步由框架代劳:内部一个 watch 比较数组引用与长度,变了就替你 调 setList。vanilla 没有那一层,所以这是唯一的入口。

收到通知后,库对渲染池里已有的项调 updateItem,把最新的 item 交回来。这个回调必须由你 提供,否则同一个 key 的项内容会停在挂载那一刻:

js
new VirtList(container, {
  list,
  itemKey: 'id',
  renderItem: (item, index, el) => paint(item, el),
  updateItem: (item, index, el) => paint(item, el),  // 常常就是同一个函数
});

之所以要分成两个回调而不是重跑 renderItem:后者的返回值会被 appendChild, 复用时再调一次会重复追加子节点。updateItem 返回 void,语义就是「原地更新」。

滚动与尺寸上报不会触发 updateItem(那是每帧都在发的),所以不必担心影响滚动性能。

三种更新手段的分工

手段什么时候用范围与代价
setList() + updateItem数据换了新数组可视区内每一项,原地更新内容,DOM 全部复用
refreshItems([key])改了数据又没走 setList只动指定的那几项,重建它们的 DOM
forceUpdate()兜底:不确定哪几项变了,或所有项的高度同时变了(如批量折叠)全部可见项,重建 DOM

forceUpdate() 是范围最大的一个,重建可见项的全部 DOM。能指明 key 时优先用 refreshItems([key]),长列表里差别看得出来。

原地修改会「偶尔看起来能工作」

库持有的就是你传进来的那个数组对象,原地改动到的就是它。所以只要随后恰好发生一次滚动或 尺寸变化,新数据就会冒出来——但没有那次触发时它不会。这种时好时坏比彻底不工作更难查, 所以改完数据请明确走 setList()refreshItems() 之一。

微应用尚未挂载。