你是说只读属性正在拖累我的性能吗?
Are you telling me a readonly property is wrecking my performance?

原始链接: https://shub.club/writings/2026/july/check-your-scrollheight/

在调查 Letta Desktop 随消息量增加而出现的性能下降问题时,作者发现其自动滚动的方法(即不断访问 `scrollTop` 和 `scrollHeight` 属性)正是性能瓶颈所在。 作者最初认为 `scrollHeight` 是一个静态且轻量的属性,但深入研究 Chromium 源代码后发现,每次访问该属性时,浏览器为了确保数值准确,都会触发样式和布局的重新计算。由于在每条新消息到达时都强制浏览器执行这些昂贵的布局计算,导致应用性能受到显著影响。 作者提醒开发者,不要理所当然地认为“只读”属性总是高性能的,特别是那些本质上依赖于当前 DOM 状态的属性。解决方案很简单:不再请求容器精确的 `scrollHeight`,而是将 `scrollTop` 设置为一个足够大的数值。浏览器会自动将其钳制(clamp)在元素底部,而不会触发昂贵的布局重排。

这篇 Hacker News 讨论探讨了看似微不足道的 JavaScript 操作如何影响性能。讨论集中在三个关键领域: * **布局抖动 (Layout Thrashing):** 用户证实读取与布局相关的属性开销很大。访问这些值会强制浏览器触发同步布局过程,这也是虚拟 DOM 库常用于批量处理这些变更的原因。 * **变量声明:** 评论者讨论了变量声明的性能开销。虽然 `var` 通常被认为是最快的,但 `let` 和 `const` 引入了需要运行时检查的“暂时性死区”(TDZ)。 * **性能权衡:** 该讨论串强调,为了代码安全或清晰而设计的现代 JavaScript 特性(如 TDZ 检查),与 `var` 等旧语法相比,可能会引入意想不到的延迟。 总而言之,此次讨论提醒我们,即使是高级 JavaScript 代码,由于浏览器的重排行为和语言层面的运行时检查,也可能产生性能损耗。
相关文章

原文

Posted on • 93 views

I was deep into investigating a performance issue with Letta Desktop lately; the story was same old same old, the greater the use, the slower the product. This seemed pretty obvious to me probably just some long running for loop, or unoptimized code.

What came to my realization was that actually the issue has nothing to do with my code (well, it did still, but, it was based on a bad assumption). The issue had to do with how I was forcing the UI to scroll down on new messages.

You see, I was using a simple script:

el.scrollTop = el.scrollHeight;

Seems reasonable, seems performant right.

If you look at the specification of scrollHeight, it seems like this property is static, and only updated on paint itself, not when accessing it.

I was wrong, of course, this is not performant, why would we set a property every time an element is modified, it makes more sense to calculate it when the user requests it.

There then lies the problem, the scrollHeight will always change when new messages come in, and if we get a ton of messages streaming in and constant need to scroll to the bottom, its gonna slow us down.

int Element::scrollHeight() {
  if (!InActiveDocument())
    return 0;
  GetDocument().UpdateStyleAndLayoutForNode(this);
  if (GetDocument().ScrollingElementNoLayout() == this) {
    if (GetDocument().View()) {
      return AdjustForAbsoluteZoom::AdjustInt(
          GetDocument().View()->LayoutViewport()->ContentsSize().Height(),
          GetDocument().GetFrame()->PageZoomFactor());
    }
    return 0;
  }
  if (LayoutBox* box = GetLayoutBox()) {
    return AdjustForAbsoluteZoom::AdjustInt(box->PixelSnappedScrollHeight(),
                                            box);
  }
  return 0;
}

(The chromium implementation of scrollHeight here)

Now, I guess if it was implmented the otherway around, it would still have the same issue I guess, but I think for me, I just assumed that readonly properties will always be pretty performant in general.

The word of advice is really this:

Properties can be dynamic, especailly when it's kinda obvious that they are like one that changes when something happens.

My solution for the scroll thing though was just put a really large number instead of trying to calculate a precise scrollheight; it will bound anyway.

联系我们 contact @ memedata.com