Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Lazy image loading never defers: IntersectionObserver runs before the placeholder is sized

Đang mở Phù hợp với người mới
#9,438 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
76/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
javascript
Lĩnh vực
frontend

Hướng nghiên cứu

Đọc src/nodes/ImageView.vue, đặc biệt là mounted() và loadAttachmentMetadata(). Issue xác định việc chờ tải siêu dữ liệu trước khi tạo IntersectionObserver là cách sửa được đề xuất. Tái hiện vấn đề với nhiều hình ảnh và xác minh rằng ban đầu chỉ những hình ảnh gần khung nhìn được tải. Sau đó cuộn trang để xác nhận các hình ảnh còn lại được tải.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

0. Needs triage bug

Describe the bug

Every image in a document is downloaded as soon as the document opens, regardless of whether it is on screen. On a remote server this makes a document with a handful of images take many seconds to become usable.

Text already implements lazy loading (see #4519). This bug asked for the fix and was closed in May 2025. So the IntersectionObserver is there in ImageView.vue. But it never gets the chance to defer anything.

In src/nodes/ImageView.vue (line 303 on main), mounted() calls loadAttachmentMetadata() without awaiting it:

mounted() {
    this.attachmentPromise = this.$attachmentResolver.resolve(this.src)
    this.loadAttachmentMetadata()      // not awaited
    ...
    this.$nextTick(() => { /* IntersectionObserver created here */ })

The placeholder's height comes from the photos-size metadata that this call resolves and the observer judges visibility from that height. Because it is not awaited, the observer runs while every figure is still a 16px stub stacked inside the viewport. Because of this, all of them count as visible and every image is loaded.

The fix is to use async mounted() and await this.loadAttachmentMetadata(). How we measured it is below.

Related: without the Photos app there is no photos-size metadata at all, so no placeholder is sized and lazy loading cannot work regardless of this bug. The code anticipates this ("bump up to warn when the Photos dependency is gone"), but the degradation currently is silent.

To Reproduce

  1. Enable the Photos app, so images carry photos-size metadata. Upload the images after enabling it.
  2. Create a document containing ~6 images of a few MB each, tall enough that most are several screens below the fold.
  3. In devtools, throttle the network to roughly 150 ms RTT / 10 Mbps.
  4. Open the document with the Network tab recording.
  5. Every image is requested immediately, including ones far below the fold.

Expected behavior

The code requests only the images at or near the viewport when the note is opened. The rest load as they are scrolled into view. This is my understand of what IntersectionObserver in ImageView.vue is written to accomplish.

Screenshots

Not applicable because the useful evidence is the timing, in the Logs section below.

Server details:

  • Nextcloud version: 35.0.0 (35.0.0.10)
  • PHP Version: 8.4.26
  • Database: PostgreSQL 18.6
  • Text app version: 9.0.0 (the same mounted() is present on main)
  • Photos app version: 8.0.0

Client details:

  • OS: macOS
  • Browser: Chromium (headless, via Playwright, for reproducible timing)
  • Browser version: 153.0.8010.12
  • Device: desktop
Logs
Nextcloud log (data/nextcloud.log)

Nothing relevant — this is entirely client-side.

Browser log

Network tab, document with six 2.5 MB images, 150 ms RTT / 10 Mbps:

BEFORE (stock Text 9.0.0)

  • 6 image requests, all starting together, 15.15 MB total
  • document ready: 14,384 ms

AFTER (await the metadata before creating the observer)

  • 2 image requests, 5.05 MB total
  • document ready: 6,426 ms
  • scrolling through the rest: all 6 fetched and rendered, 0 failures

Polling each figure's height against the request start times shows the race:

  • 790 ms 6 figures, heights [16,16,16,16,16,16] <- all inside the viewport
  • 792 ms all six image requests start <- observer fires for all
  • 804 ms 6 figures, heights [513,513,513,513,513,513] <- 4 of 6 now below the fold

The sizes that would have deferred four of the six arrive 12 ms after the decision has been made and the observers disconnected.


Diagnosis and measurements were produced with the help of Claude Code.

Ngôn ngữ chính
JavaScript
Star
665
Fork
133
Merge trung bình
1 ngày 17 giờ
Pull request đã merge (30 ngày)
172

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của nextcloud/text

Tất cả issue của nextcloud/text

Issue tương tự

Thêm issue về JavaScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.