Sonsuz kaydırıcının karmaşıklıkları

Özet: DOM öğelerinizi yeniden kullanın ve görüntü alanından çok uzakta olanları kaldırın. Gecikmeli verileri hesaba katmak için yer tutucuları kullanın. Sonsuz kaydırma için demo ve kod.

Sonsuz kaydırma özelliği internetin her yerinde karşımıza çıkıyor. Google Music'in sanatçı listesi, Facebook'un zaman çizelgesi ve Twitter'ın canlı feed'i de tek bir feed'dir. Aşağı kaydırdığınızda, sayfanın en altına ulaşmadan yeni içerikler sanki sihirli bir şekilde belirir. Kullanıcılar için sorunsuz bir deneyim sunan bu özelliğin cazibesini anlamak kolaydır.

Ancak sonsuz kaydırma özelliğinin arkasındaki teknik zorluk, göründüğünden daha fazladır. Doğru Şeyi Yapmak™ istediğinizde karşılaşacağınız sorunların kapsamı çok geniştir. İçerik, altbilgiyi sürekli olarak ittiği için altbilgideki bağlantıların neredeyse ulaşılamaz hale gelmesi gibi basit sorunlarla başlar. Ancak sorunlar zorlaşır. Bir kullanıcı telefonunu dikeyden yataya çevirdiğinde yeniden boyutlandırma etkinliğini nasıl ele alırsınız veya liste çok uzadığında telefonunuzun acı verici bir şekilde durmasını nasıl önlersiniz?

The right thing™

Bu nedenle, performans standartlarını korurken tüm bu sorunları yeniden kullanılabilir bir şekilde ele almanın bir yolunu gösteren bir referans uygulama oluşturmanın yeterli bir neden olduğunu düşündük.

Hedefimize ulaşmak için 3 teknik kullanacağız: DOM geri dönüşümü, tombstones ve kaydırma sabitleme.

Demo örneğimiz, mesajlar arasında kaydırabileceğimiz Hangouts benzeri bir sohbet penceresi olacak. İlk olarak, sonsuz bir sohbet mesajı kaynağına ihtiyacımız var. Teknik olarak, sonsuz kaydırma özelliği sunan hiçbir site gerçekten sonsuz değildir ancak bu kaydırma özelliklerine aktarılabilecek veri miktarı o kadar fazladır ki bu siteler sonsuzmuş gibi görünür. Basit olması için bir dizi sohbet mesajını sabit kodlayıp mesajı, yazarı ve ara sıra resim ekini rastgele seçeceğiz. Ayrıca, gerçek ağa biraz daha benzemesi için yapay gecikme de ekleyeceğiz.

Sohbet uygulaması ekran görüntüsü

DOM geri dönüşümü

DOM geri dönüşümü, DOM düğümü sayısını düşük tutmak için yeterince kullanılmayan bir tekniktir. Genel fikir, yeni DOM öğeleri oluşturmak yerine ekran dışında bulunan, daha önce oluşturulmuş DOM öğelerini kullanmaktır. DOM düğümleri ucuz olsa da ücretsiz değildir. Her biri bellek, düzen, stil ve boyama açısından ek maliyet getirir. Web sitesinin yönetilemeyecek kadar büyük bir DOM'u varsa düşük seviye cihazlar, tamamen kullanılamaz hale gelmese de belirgin şekilde yavaşlar. Ayrıca, her yeniden düzenleme ve stillerinizin yeniden uygulanmasının (bir sınıfa düğüm eklendiğinde veya sınıftan düğüm kaldırıldığında tetiklenen bir süreç) daha büyük bir DOM ile daha maliyetli hale geldiğini unutmayın. DOM düğümlerinizi geri dönüştürmek, toplam DOM düğümü sayısını önemli ölçüde düşük tutacağımız ve bu sayede tüm bu süreçleri hızlandıracağımız anlamına gelir.

İlk engel, kaydırmanın kendisidir. DOM'da herhangi bir zamanda yalnızca mevcut öğelerin küçük bir alt kümesi olacağından, tarayıcının kaydırma çubuğunun teorik olarak mevcut içerik miktarını doğru şekilde yansıtmasını sağlamak için başka bir yol bulmamız gerekiyor. Öğeleri içeren öğenin (podyum) istenen yüksekliğe sahip olmasını zorlamak için dönüştürme içeren 1 piksel x 1 piksel boyutunda bir sentinel öğesi kullanacağız. Podyumun kendisinin tamamen boş olduğundan emin olmak için podyumdaki her öğeyi kendi katmanına yükseltiriz. Arka plan rengi yok, hiçbir şey yok. Pist katmanı boş değilse tarayıcının optimizasyonlarına uygun değildir ve grafik kartımızda birkaç yüz bin piksel yüksekliğinde bir doku depolamamız gerekir. Mobil cihazda kesinlikle uygun değildir.

Kaydırma yaptığımızda, görünüm alanının pistin sonuna yeterince yaklaşıp yaklaşmadığını kontrol ederiz. Bu durumda, sentinel öğesini taşıyarak ve görüntü alanından çıkan öğeleri pistin en altına taşıyıp yeni içeriklerle doldurarak pisti uzatırız.

Runway Sentinel Viewport

Aynı durum diğer yönde kaydırma için de geçerlidir. Ancak kaydırma çubuğu konumunun tutarlı kalması için uygulamamızda hiçbir zaman pisti kısaltmayacağız.

Tombstone'lar

Daha önce de belirttiğimiz gibi, veri kaynağımızın gerçek dünyadaki bir şey gibi davranmasını sağlamaya çalışıyoruz. Ağ gecikmesi ve diğer tüm sorunlar. Bu, kullanıcılarımızın hızlı kaydırma özelliğini kullanması durumunda, verilerimizin bulunduğu son öğeyi kolayca geçebileceği anlamına gelir. Bu durumda, veriler geldiğinde gerçek içerikli öğeyle değiştirilecek bir yer tutucu olan mezar taşı öğesi yerleştiririz. Tombstone'lar da geri dönüştürülür ve yeniden kullanılabilir DOM öğeleri için ayrı bir havuzda tutulur. Bu sayede, kullanıcının dikkatini dağıtacak ve odaklandığı şeyi unutmasına neden olabilecek bir geçiş yerine, yer tutucudan içerikle doldurulmuş öğeye sorunsuz bir geçiş yapabiliriz.

Such
tomb. Çok taşlı. Vay canına!

Buradaki ilginç zorluk, öğe başına farklı miktarlarda metin veya eklenmiş bir resim nedeniyle gerçek öğelerin, yer işareti öğesinden daha yüksek olabilmesidir. Bu sorunu çözmek için, her veri geldiğinde ve görünüm alanının üzerinde bir mezar taşı değiştirildiğinde mevcut kaydırma konumunu ayarlayacağız. Böylece kaydırma konumu bir piksel değeri yerine bir öğeye sabitlenecek. Bu kavram, kaydırma sabitleme olarak adlandırılır.

Kaydırma sabitleme

Kaydırma sabitleme özelliğimiz, hem yer işaretleri değiştirilirken hem de pencere yeniden boyutlandırılırken (bu işlem cihaz döndürülürken de gerçekleşir) etkinleştirilir. Görüntü alanındaki en üstte görünen öğenin ne olduğunu bulmamız gerekir. Bu öğe yalnızca kısmen görünür olabileceğinden, görüntü alanının başladığı yerdeki öğenin üst kısmından olan uzaklığı da saklarız.

Kaydırma sabitleme diyagramı.

Görünüm penceresi yeniden boyutlandırılırsa ve pistte değişiklikler olursa kullanıcıya görsel olarak aynı hissi veren bir durumu geri yükleyebiliriz. Kazandınız! Ancak yeniden boyutlandırılmış bir pencere, her öğenin yüksekliğinin değişmiş olabileceği anlamına gelir. Bu durumda, sabitlenmiş içeriğin ne kadar aşağıya yerleştirilmesi gerektiğini nasıl bilebiliriz? Hayır! Bunu bulmak için sabitlenmiş öğenin üzerindeki her öğeyi yerleştirip tüm yüksekliklerini toplamamız gerekir. Bu durum, yeniden boyutlandırmadan sonra önemli bir duraklamaya neden olabilir ve bunu istemiyoruz. Bunun yerine, yukarıdaki her öğenin bir mezar taşıyla aynı boyutta olduğunu varsayarak kaydırma konumumuzu buna göre ayarlıyoruz. Öğeler pistte kaydırıldıkça kaydırma konumumuzu ayarlayarak düzenleme çalışmasını gerçekten ihtiyaç duyulduğu zamana erteliyoruz.

Düzen

Önemli bir ayrıntıyı atladım: Düzen. Bir DOM öğesinin her geri dönüşümü normalde tüm pisti yeniden düzenlerdi. Bu da bizi saniyede 60 kare hedefimizin çok altına düşürürdü. Bunu önlemek için düzen yükünü üstleniyor ve dönüştürmelerle mutlak konumlandırılmış öğeler kullanıyoruz. Bu sayede, pistin daha yukarıdaki tüm öğelerin aslında boş alan olmasına rağmen yer kapladığını düşünebiliriz. Düzeni kendimiz oluşturduğumuz için her öğenin yerleştiği konumları önbelleğe alabiliriz. Kullanıcı geriye doğru kaydırdığında ise önbellekten doğru öğeyi hemen yükleyebiliriz.

İdeal olarak, öğeler DOM'a eklendiklerinde yalnızca bir kez yeniden boyanır ve pistteki diğer öğelerin eklenmesinden veya kaldırılmasından etkilenmez. Bu mümkündür ancak yalnızca modern tarayıcılarda kullanılabilir.

En yeni ayarlamalar

Chrome kısa süre önce CSS Containment desteği ekledi. Bu özellik, geliştiriciler olarak tarayıcıya bir öğenin düzen ve boyama çalışması için sınır olduğunu söylememize olanak tanır. Burada düzeni kendimiz yaptığımız için bu, kapsama için önemli bir uygulamadır. Piste bir öğe eklediğimizde diğer öğelerin yeniden düzenlemeden etkilenmesine gerek olmadığını biliyoruz. Bu nedenle her öğe contain: layout olmalıdır. Web sitemizin geri kalanını da etkilemek istemiyoruz. Bu nedenle, pistin kendisi de bu stil yönergesini almalıdır.

Dikkate aldığımız bir diğer nokta ise kullanıcının öğeleri geri dönüştürmeye ve yeni veriler yüklemeye başlamamız için yeterince kaydırdığını tespit etmek amacıyla IntersectionObservers kullanmaktı. Ancak IntersectionObserver'ların yüksek gecikmeli olduğu (requestIdleCallback kullanılıyormuş gibi) belirtilir. Bu nedenle, IntersectionObserver'lar kullanıldığında, kullanılmadığı duruma kıyasla daha az duyarlı olduğunu hissedebiliriz. Kaydırma etkinlikleri "en iyi çaba" esasına göre gönderildiğinden, scroll etkinliğini kullanan mevcut uygulamamız bile bu sorundan etkileniyor. Sonunda, Houdini'nin Compositor Worklet'i bu soruna yüksek doğrulukta bir çözüm sunacaktı.

Hâlâ mükemmel değil

DOM geri dönüşümünün mevcut uygulaması, yalnızca ekranda olan öğelerle ilgilenmek yerine, görüntü alanından geçen tüm öğeleri eklediği için ideal değildir. Bu nedenle, çok hızlı kaydırdığınızda Chrome'un düzen ve boyama için çok fazla çalışması gerekir ve bu da Chrome'un yetişememesine neden olur. Sonuç olarak, arka plan dışında hiçbir şey göremezsiniz. Bu durum dünyanın sonu olmasa da kesinlikle iyileştirilmesi gereken bir noktadır.

Basit sorunların, mükemmel bir kullanıcı deneyimini yüksek performans standartlarıyla birleştirmek istediğinizde ne kadar zorlayıcı olabileceğini anladığınızı umuyoruz. Progresif web uygulamaları mobil telefonlarda temel deneyimler haline geldiğinden bu durum daha da önem kazanacak ve web geliştiricilerin performans kısıtlamalarına uyan kalıpları kullanmaya yatırım yapmaya devam etmesi gerekecek.

Tüm kodları depomuzda bulabilirsiniz. Yeniden kullanılabilir olması için elimizden geleni yaptık ancak npm'de gerçek bir kitaplık olarak veya ayrı bir depo olarak yayınlamayacağız. Birincil kullanım amacı eğitimdir.