性能:大文档的节流渲染与增量更新

目标

几百页的文档,每次 input 都全量重渲染会卡。本节课写一个**节流(throttle)**工具(Node 可运行),理解"高频事件要限流";再讲编辑器真正的性能法宝:增量更新。

运行环境:Node.js v18+。运行方式:保存为 throttle.js,执行 node throttle.js。

// throttle.js —— 限流高频事件:最多每 ms 毫秒执行一次
function throttle(fn, ms) {
  let last = 0
  let timer = null
  return (...args) => {
    const now = Date.now()
    const remain = ms - (now - last)
    if (remain <= 0) {
      last = now
      fn(...args)
    } else if (!timer) {
      timer = setTimeout(() => { last = Date.now(); timer = null; fn(...args) }, remain)
    }
  }
}

// ===== 调用示例:模拟 1 秒内狂触发 20 次,渲染只跑几次 =====
let count = 0
const render = throttle(() => { count++; console.log('渲染第', count, '次 @', Date.now()) }, 200)
for (let i = 0; i < 20; i++) render()
setTimeout(() => console.log('1 秒后共渲染', count, '次(被节流)'), 1000)

throttle 保证 render 在 200ms 内最多执行一次,把"狂触发"压成"按节奏跑"。

名词解释

节流(Throttle):限制函数在一段时间窗口内最多执行一次,用于驯服高频事件(输入、滚动、缩放)。
防抖(Debounce):与节流不同,防抖是"停止触发 N 毫秒后才执行一次"(适合搜索框)。本课用的是节流(适合持续渲染)。
增量更新(Incremental Update):只重渲染"变了的那部分 DOM",而非整篇。是大型编辑器不卡的根本原因,比节流更彻底。

课后练习

练习:写一句区分"节流"和"防抖"的话,并各举一个适用场景。

答案:节流=匀速限流(每 200ms 最多一次),适合持续渲染/滚动打点;防抖=静默后才跑(停下 200ms 才执行),适合搜索联想、窗口 resize 后的布局重算。一句话:节流管"频率上限",防抖管"静默触发"。

总结

性能是编辑器"能不能用"的硬指标。节流是最低成本的优化——把高频 input 压成匀速渲染,立竿见影。但它只是"限流",不是"少做":每次仍然可能全量渲染。大型编辑器(Google Docs、Notion)的真正法宝是增量更新:模型变了,只把"变化的节点"对应的 DOM 局部更新,未变的子树纹丝不动。这要求模型是结构化的(回到第4/8节的 JSON 模型)——只有能精确 diff"哪里变了",才能精确只更新那里。所以性能不是孤立的一节,它是前面"模型与视图分离"的自然回报:有了干净模型,增量更新才成为可能。