Kompleksitas scroller tanpa batas

TL;DR: Gunakan kembali elemen DOM Anda dan hapus elemen yang jauh dari area pandang. Gunakan placeholder untuk memperhitungkan data yang tertunda. Berikut adalah demo dan code untuk .

Scroller tanpa batas muncul di seluruh internet. Daftar artis Google Musik adalah satu, {i>timeline<i} Facebook adalah satu dan {i> live feed<i} Twitter. Anda scroll ke bawah dan sebelum Anda mencapai bagian bawah, konten baru akan muncul secara ajaib tampaknya entah dari mana. Ini adalah pengalaman yang mulus bagi pengguna dan mudah melihat pengajuan banding.

Namun, tantangan teknis di balik scrollinger tanpa batas lebih sulit daripada tampaknya. Berbagai masalah yang Anda temui saat ingin melakukan The Right ThingTM sangat luas. Dimulai dengan hal-hal sederhana seperti tautan di {i>footer<i} menjadi hampir tidak dapat dijangkau karena konten terus mendorong {i>footer<i}. Tapi masalah menjadi lebih sulit. Bagaimana Anda menangani peristiwa ubah ukuran saat seseorang mengubah ponsel dari potret ke lanskap atau bagaimana Anda mencegah ponsel grinding sampai akhir yang menyakitkan ketika daftarnya menjadi terlalu panjang?

Yang benarTM

Kami rasa hal tersebut merupakan alasan yang cukup untuk menghasilkan penerapan referensi yang menunjukkan cara untuk mengatasi semua masalah ini dengan cara yang dapat digunakan kembali menjaga standar performa.

Kita akan menggunakan 3 teknik untuk mencapai tujuan: daur ulang DOM, tombstone dan scroll anchor.

Kasus demo kita adalah jendela obrolan seperti Hangouts di mana kita dapat menggulir melalui pesan. Hal pertama yang kami butuhkan adalah sumber chat yang tak terbatas membuat pesan teks. Secara teknis, tidak ada scrolling tanpa batas yang benar-benar tak terbatas, tetapi dengan jumlah data yang tersedia untuk dipompa ke dalam {i>scroller<i} yang serupa. Demi kemudahan, kita hanya akan melakukan hard code serangkaian pesan obrolan dan pilih pesan, penulis, dan lampiran gambar sesekali di acak dengan taburan penundaan buatan untuk berperilaku sedikit lebih seperti jaringan yang nyata.

Screenshot aplikasi Chat

Daur ulang DOM

Daur ulang DOM adalah teknik yang kurang dimanfaatkan untuk menjaga jumlah node DOM tetap rendah. Tujuan ide umumnya adalah menggunakan elemen DOM yang sudah dibuat di luar layar sebagai gantinya membuat yang baru. Memang, simpul DOM itu sendiri murah, tapi tidak gratis, karena masing-masing menambah biaya ekstra dalam memori, {i>layout<i}, gaya, dan {i>cat<i}. Perangkat {i>low-end <i}akan menjadi lebih lambat jika tidak dapat digunakan sepenuhnya jika situs Anda memiliki DOM yang terlalu besar untuk dikelola. Juga perlu diingat bahwa setiap tata letak ulang dan penerapan ulang gaya Anda – sebuah proses yang dipicu setiap kali sebuah class ditambahkan atau dihapus dari node – menjadi lebih mahal dengan DOM yang lebih besar. Mendaur ulang node DOM berarti kita akan mempertahankan jumlah total DOM {i>node<i} secara signifikan lebih rendah, sehingga membuat semua proses ini lebih cepat.

Hambatan pertama adalah scroll itu sendiri. Karena kita hanya akan memiliki {i>subset<i} kecil semua item yang tersedia di DOM pada waktu tertentu, kita perlu mencari cara lain agar scrollbar browser mencerminkan jumlah konten yang secara teoritis. Kita akan menggunakan elemen sentinel 1px kali 1px dengan transformasi untuk memaksa elemen yang berisi item - landasan pacu - agar memiliki elemen yang tinggi. Kami akan mempromosikan setiap elemen di landasan ke {i>layer<i} mereka sendiri untuk membuat memastikan lapisan landasan pacu itu sendiri benar-benar kosong. Tanpa warna latar belakang, tidak terjadi apa-apa. Jika lapisan landasan tidak kosong, lapisan tersebut tidak memenuhi syarat untuk pengoptimalan dan kita harus menyimpan tekstur pada kartu grafis yang memiliki tingginya beberapa ratus ribu piksel. Jelas tidak cocok pada perangkat seluler.

Setiap kali men-scroll, kita akan memeriksa apakah area pandang mendekati ujung landasan. Jika demikian, kami akan memperpanjang landasan dengan memindahkan sentinel dan memindahkan item yang telah meninggalkan area pandang ke bagian bawah landasan pacu dan mengisinya dengan konten baru.

Lintasan Penjaga Area pandang

Hal yang sama berlaku untuk men-scroll ke arah lain. Namun, kita tidak akan pernah mengecilkan landasan dalam implementasi kita, sehingga posisi scrollbar tetap konsisten.

Tombstone

Seperti yang kami sebutkan sebelumnya, kita mencoba membuat sumber data berfungsi seperti di dunia nyata. Dengan latensi jaringan dan lainnya. Itu berarti bahwa jika pengguna memanfaatkan {i>scrolling<i}, mereka dapat dengan mudah menggulir melewati elemen terakhir yang datanya kita miliki. Jika itu terjadi, kita akan menempatkan item tombstone – sebuah - yang akan digantikan oleh item dengan konten aktual setelah data telah tiba. Batu nisan juga didaur ulang dan memiliki kolam terpisah untuk elemen DOM yang dapat digunakan kembali. Kita membutuhkannya agar dapat membuat transisi yang baik dari ke item yang diisi dengan konten, yang seharusnya akan sangat mengagetkan pengguna dan mungkin benar-benar membuat mereka kehilangan jejak kita fokuskan.

Seperti
Makam ini. Benar-benar batu. Wow.

Tantangan menarik di sini adalah item sungguhan bisa memiliki tinggi lebih besar dari item tombstone karena perbedaan jumlah teks per item atau gambar. Untuk mengatasinya, kita akan menyesuaikan posisi scroll saat ini setiap kali data masuk dan tombstone akan diganti di atas area tampilan, yang merupakan anchoring posisi scroll ke elemen, bukan ke nilai piksel. Konsep ini adalah yang disebut scroll anchoring.

Scroll anchor

Anchor scroll akan dipanggil saat tombstone diganti saat serta ketika ukuran jendela diubah (yang juga terjadi ketika perangkat dibalik!). Kita harus mencari tahu elemen apa yang paling terlihat area pandang berada. Karena elemen itu hanya bisa terlihat sebagian, kita juga akan menyimpan offset dari atas elemen tempat area pandang dimulai.

Diagram anchor scroll.

Jika area pandang diubah ukurannya dan landasan pacu mengalami perubahan, kita bisa memulihkan situasi yang secara visual terasa identik dengan pengguna. Menang! Kecuali diubah ukurannya berarti setiap item berpotensi mengalami perubahan tinggi, jadi bagaimana kita mengetahui seberapa jauh konten yang ditautkan harus ditempatkan di bawah? Kami tidak bisa melakukannya. Untuk mengetahui kita harus menata letak setiap elemen di atas item yang ditambatkan dan menjumlahkan semua tinggi mereka; hal ini dapat menyebabkan jeda yang signifikan setelah perubahan ukuran, dan kami tidak menginginkannya. Sebaliknya, kami mengambil asumsi bahwa setiap item di atas memiliki ukuran yang sama sebagai batu nisan dan menyesuaikan posisi scroll kita. Karena elemen men-scroll di landasan pacu, kita menyesuaikan posisi scroll, secara efektif tata letak berfungsi ketika benar-benar dibutuhkan.

Tata Letak

Saya melewatkan detail penting: Tata letak. Setiap daur ulang elemen DOM biasanya akan menata ulang seluruh landasan pacu yang akan membawa kita jauh di bawah target 60 frame per detik. Untuk menghindari hal ini, kami menanggung {i>layout<i} ke diri kita sendiri dan menggunakan elemen yang benar-benar diposisikan dengan {i>transformasi<i}. Dengan cara ini kita dapat berpura-pura bahwa semua elemen di landasan pacu masih memakan ruang padahal sebenarnya hanya ada ruang kosong. Karena yang kita lakukan sendiri, kita bisa meng-cache posisi di mana setiap item berakhir dan kita bisa segera memuat elemen yang benar dari cache ketika pengguna men-scroll mundur.

Idealnya, item hanya akan dicat ulang sekali ketika dilampirkan ke DOM dan tidak terpengaruh oleh penambahan atau penghapusan barang lain di landasan pacu. Itu adalah mungkin, namun hanya dengan {i>browser<i} modern.

Penyesuaian pendarahan

Baru-baru ini, Chrome menambahkan dukungan untuk Penahanan CSS, sebuah fitur yang memungkinkan pengembang memberi tahu browser bahwa suatu elemen merupakan batas untuk tata letak dan pekerjaan cat. Karena kita melakukan tata letak sendiri di sini, untuk penanggulangan. Setiap kali menambahkan elemen ke landasan, kita tahu item lain tidak perlu terpengaruh oleh tata letak ulang. Jadi setiap item harus mendapatkan contain: layout. Kami juga tidak ingin mempengaruhi bagian lain dari situs web kami, jadi landasan pacu itu sendiri harus mendapatkan perintah gaya ini juga.

Hal lain yang kami pertimbangkan adalah menggunakan IntersectionObservers sebagai mekanisme untuk mendeteksi kapan pengguna telah men-scroll cukup jauh sehingga kita dapat mulai mendaur ulang elemen dan memuat layanan otomatis dan data skalabel. Namun, IntersectionObservers ditetapkan sebagai latensi tinggi (seolah-olah menggunakan requestIdleCallback), jadi kami mungkin sebenarnya terasa kurang responsif dengan IntersectionObservers daripada tanpa IntersectionObserver. Bahkan implementasi kita saat ini dengan menggunakan Peristiwa scroll mengalami masalah ini, karena peristiwa scroll dikirim pada “upaya terbaik”. Akhirnya, Worklet Compositor Houdini adalah solusi {i>high-fidelity <i}untuk masalah ini.

Ini masih belum sempurna

Implementasi daur ulang DOM kita saat ini tidak ideal karena menambahkan semua elemen yang melewati area pandang, bukan hanya peduli dengan hal-hal yang sebenarnya di layar. Artinya, saat men-scroll dengan sangat cepat, Anda memasukkan begitu banyak pekerjaan tata letak dan penggambaran di Chrome sehingga tidak dapat mengimbangi hal itu. Anda akan mengakhiri hanya melihat latar belakang. Ini bukanlah kiamat, tetapi pasti sesuatu yang perlu diperbaiki.

Kami harap Anda melihat betapa sulitnya masalah sederhana ketika Anda ingin menggabungkan pengalaman pengguna yang hebat dengan standar kinerja tinggi. Dengan Progressive Web App menjadi pengalaman inti di ponsel, ini akan menjadi lebih penting dan pengembang web harus terus berinvestasi dalam menggunakan pola yang mengikuti batasan performa.

Semua kode dapat ditemukan di repositori kami. Kita telah melakukan upaya terbaik untuk menjaganya agar tetap dapat digunakan kembali, tetapi tidak akan memublikasikannya sebagai library yang sebenarnya di npm atau sebagai repositori terpisah. Penggunaan utamanya adalah untuk edukasi.