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

Özet: DOM öğelerinizi yeniden kullanın ve görüntü alanından uzak olanları kaldırın. Gecikmeli verileri hesaba katmak için yer tutucular kullanın. Sonsuz kaydırma çubuğu için demo ve kod aşağıda verilmiştir.

Sonsuz kaydırma özelliğine sahip uygulamalar internette her yerde karşınıza çıkar. Google Music'in sanatçı listesi, Facebook'un zaman çizelgesi ve Twitter'ın canlı feed'i de birer feed'dir. Sayfayı aşağı kaydırdığınızda, en alta ulaşmadan önce yeni içerikler sanki hiç yoktan ortaya çıkıyor. Kullanıcılar için sorunsuz bir deneyim sunar ve bu özelliğin cazibesini anlamak kolaydır.

Ancak sonsuz kaydırma çubuğunun teknik zorluğu göründüğünden daha fazladır. Doğru olanı yapmak istediğinizde karşılaştığınız sorunların yelpazesi çok geniştir. Bu durum, içeriklerin altbilgiyi itmeye devam etmesi nedeniyle altbilgideki bağlantıların neredeyse erişilemez hale gelmesi gibi basit şeylerle başlar. Ancak sorunlar daha da zorlaşıyor. Bir kullanıcı telefonunu dikey moddan yatay moda geçirdiğinde yeniden boyutlandırma etkinliğini nasıl ele alırsınız? Liste çok uzun olduğunda telefonunuzun yavaşlaması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 geliştirmenin yeterli bir neden olduğunu düşündük.

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

Demo destek kaydımız, mesajlar arasında gezinebileceğimiz Hangouts benzeri bir sohbet penceresi olacak. İlk olarak sonsuz bir sohbet mesajı kaynağına ihtiyacımız var. Teknik olarak, mevcut sonsuz kaydırma listelerinin hiçbiri gerçekten sonsuz değildir ancak bu kaydırma listelerine aktarılabilen veri miktarı sayesinde sonsuz olabilirler. Basitlik açısından, bir dizi sohbet mesajını sabit kodlayacağız ve mesajı, yazarı ve ara sıra resim eklerini rastgele seçeceğiz. Gerçek ağa biraz daha benzemesi için de yapay gecikme ekliyoruz.

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 ekranda olmayan, önceden oluşturulmuş DOM öğelerini kullanmaktır. DOM düğümlerinin kendileri ucuz olsa da her biri bellek, düzen, stil ve boya için ek maliyet eklediğinden ücretsiz değildir. Web sitesinin yönetilmesi için çok büyük bir DOM'u varsa düşük kaliteli cihazlar tamamen kullanılamaz hale gelmese de belirgin şekilde yavaşlar. Ayrıca, bir sınıf bir düğüme eklendiğinde veya düğümden kaldırıldığında tetiklenen bir işlem olan stillerinizin her yeniden düzenlenmesinin ve yeniden uygulanmasının, DOM'un boyutu arttıkça daha pahalı hale geldiğini unutmayın. DOM düğümlerinizi geri dönüştürmek, toplam DOM düğümü sayısını önemli ölçüde azaltacağımız anlamına gelir. Bu da tüm bu süreçleri hızlandırır.

İlk engel, kaydırma işleminin kendisidir. Belirli bir zamanda DOM'daki tüm öğelerin yalnızca küçük bir alt kümesine sahip olacağız. Bu nedenle, tarayıcının kaydırma çubuğunun teorik olarak mevcut olan içerik miktarını doğru şekilde yansıtmasını sağlamak için başka bir yol bulmamız gerekiyor. Öğeleri içeren öğeyi (pist) istenen yüksekliğe zorlamak için bir dönüştürme özelliğine sahip 1 piksel x 1 piksel bir gözcü öğesi kullanacağız. Podyumun katmanının tamamen boş olduğundan emin olmak için podyumdaki her öğeyi kendi katmanına taşıyoruz. Arka plan rengi yok, hiçbir şey yok. Pist katmanı boş değilse tarayıcı optimizasyonları için uygun değildir ve grafik kartımızda birkaç yüz bin piksel yüksekliğinde bir doku depolamamız gerekir. Mobil cihazlarda kesinlikle uygun değildir.

Kaydırdığımızda, görüntü alanının pistin sonuna yeterince yaklaşıp yaklaşmadığını kontrol ederiz. Bu durumda, gözetleyici öğesini ve görüntü alanından çıkan öğeleri pistin alt kısmına taşıyarak pistin uzunluğunu uzatırız ve bu öğeleri yeni içerikle doldururuz.

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 aralığı küçültmeyiz.

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 her şeyle birlikte. Bu, kullanıcılarımız kaydırma çubuğuyla kaydırmayı kullanırsa verilerine sahip olduğumuz son öğeyi kolayca geçebilecekleri anlamına gelir. Bu durumda, veriler geldikten sonra gerçek içeriğe sahip öğeyle değiştirilecek bir yer tutucu öğe (mezar taşı öğesi) yerleştiririz. Mezar taşları da geri dönüştürülür ve yeniden kullanılabilir DOM öğeleri için ayrı bir havuzları vardır. Bu, kullanıcının dikkatini dağıtmayacak ve odaklandığı konuyu kaybetmesine neden olmayacak şekilde, yer işaretinden içerikle doldurulmuş öğeye güzel bir geçiş yapabilmemiz için gereklidir.

Böyle bir mezar. Çok taş. Vay canına!

Buradaki ilginç bir zorluk, öğe başına farklı metin miktarları veya ekli bir resim nedeniyle gerçek öğelerin mezar taşı öğesinden daha yüksek bir yüksekliğe sahip olabilmesidir. Bu sorunu çözmek için, veri her geldiğinde ve bir mezar taşı görüntü alanının üstünde değiştirildiğinde mevcut kaydırma konumunu ayarlayarak kaydırma konumunu bir piksel değeri yerine bir öğeye sabitleriz. Bu kavrama kaydırma sabitleme denir.

Kaydırma sabitleme

Kaydırma sabitlememiz hem mezar taşları değiştirilirken hem de pencere yeniden boyutlandırıldığında (cihazlar çevrilirken de gerçekleşir) çağrılır. 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ünebileceğinden, görüntü alanının başladığı öğenin üst kısmından itibaren ofseti de depolarız.

Kaydırma sabitleme şeması.

Görüntü alanı yeniden boyutlandırılırsa ve taksi yolu değişirse kullanıcıya görsel olarak aynı hissi veren bir durumu geri yükleyebiliriz. Kazanın. Ancak yeniden boyutlandırılmış bir pencere, her öğenin yüksekliğinin değişmiş olabileceği anlamına gelir. Bu nedenle, sabitlenmiş içeriğin ne kadar aşağıya yerleştirilmesi gerektiğini nasıl bilebiliriz? Hayır. Bunu öğrenmek için her öğeyi sabitlenmiş öğenin üzerine yerleştirip tüm yüksekliklerini toplamalıyız. Bu, 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 varsayar ve kaydırma konumumuzu buna göre ayarlarız. Öğeler kaydırma çubuğunda kaydırıldığında kaydırma konumumuzu ayarlayarak sayfa düzeni çalışmasını gerçekten ihtiyaç duyulduğunda erteliyoruz.

Düzen

Önemli bir ayrıntıyı atladım: Düzen. Bir DOM öğesinin her geri dönüşümü, genellikle tüm aralığın yeniden düzenlenmesine neden olur. Bu da saniye başına 60 karelik hedefimizin çok altına düşmemize neden olur. Bunu önlemek için düzen yükünü üstleniyoruz ve dönüşümlerle birlikte mutlak konumlandırılmış öğeler kullanıyoruz. Bu sayede, pistin daha ilerisindeki tüm öğelerin aslında boş alan olduğunda hâlâ yer kapladığını varsayabiliriz. Düzeni kendimiz yaptığımızdan, her öğenin sona erdiği konumları önbelleğe alabiliriz ve kullanıcı geri kaydırdığında doğru öğeyi önbellekten hemen yükleyebiliriz.

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

Son teknoloji ayarlamalar

Yakın zamanda Chrome, CSS İçerme özelliğini desteklemeye başladı. Bu özellik, geliştiricilerin tarayıcıya bir öğenin düzen ve boyama çalışması için sınır olduğunu söylemesine olanak tanır. Burada düzeni kendimiz yaptığımızdan, bu, kapsayıcı için mükemmel bir uygulamadır. Podyuma bir öğe eklediğimizde, diğer öğelerin yeniden düzenlemeden etkilenmesi gerekmediğini biliyoruz. Bu nedenle her öğe contain: layout almalıdır. Ayrıca web sitemizin geri kalanını etkilemek istemiyoruz. Bu nedenle, podyumun kendisi de bu stil yönergesini almalıdır.

Kullanıcının öğeleri geri dönüştürmeye ve yeni veriler yüklemeye başlamamız için yeterince kaydırdığını algılamak amacıyla IntersectionObservers'i bir mekanizma olarak kullanmayı da düşündük. Ancak IntersectionObserver'ların yüksek gecikmeli olduğu belirtilir (requestIdleCallback kullanılıyormuş gibi). Bu nedenle, IntersectionObserver'ları kullanmadan daha az duyarlı hissedebiliriz. Kaydırma etkinlikleri "en iyi çaba" temelinde gönderildiği için scroll etkinliğini kullanan mevcut uygulamamız bile bu sorundan etkileniyor. Sonunda, Houdini'nin Compositor Worklet bu sorunun yüksek kaliteli çözümü olacak.

Hâlâ mükemmel değil

Mevcut DOM geri dönüşüm uygulamamız, yalnızca ekranda bulunan öğelerle ilgilenmek yerine, görüntü alanının içinden geçen tüm öğeleri eklediği için ideal değildir. Yani çok hızlı kaydırdığınızda Chrome'un sayfa düzeni ve boyama işlemini o kadar hızlı yapmasını istersiniz ki Chrome buna yetişemez. Yalnızca arka planı görürsünüz. Bu durum dünyanın sonu değil ancak kesinlikle iyileştirilmesi gereken bir konudur.

Mükemmel bir kullanıcı deneyimini yüksek performans standartlarıyla birleştirmek istediğinizde basit sorunların ne kadar zor olabileceğini anladığınızı umuyoruz. Progresif web uygulamalarının mobil telefonlarda temel deneyim haline gelmesiyle bu durum daha da önem kazanacak ve web geliştiricilerin performans kısıtlamalarına saygı gösteren kalıplar kullanmaya yatırım yapmaya devam etmesi gerekecek.

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