इनफ़ाइनाइट स्क्रोलर की समस्याएं

रॉबर्ट फ़्लैक
रॉबर्ट फ़्लैक

कम शब्दों में लिखें: अपने DOM एलिमेंट का फिर से इस्तेमाल करें और व्यूपोर्ट से बहुत दूर मौजूद एलिमेंट को हटाएं. देरी से मिले डेटा को देखने के लिए प्लेसहोल्डर का इस्तेमाल करें. यहां एक डेमो और इनफ़ाइनाइट स्क्रोलर के लिए कोड दिया गया है.

इनफ़ाइनाइट स्क्रोलर पूरे इंटरनेट पर पॉप-अप होते हैं. Google Music की कलाकारों की सूची एक है, Facebook की टाइमलाइन एक है और Twitter की लाइव फ़ीड भी एक है. आप नीचे की ओर स्क्रोल करते हैं और नीचे पहुंचने से पहले, नया कॉन्टेंट अपने-आप कहीं से भी दिखने लगता है. इससे लोगों को बेहतरीन अनुभव मिलता है और लोगों की अपील आसानी से देखी जा सकती है.

हालांकि, एक इनफ़ाइनाइट स्क्रोलर के पीछे की तकनीकी चुनौती, जितनी मुश्किल लगती है उससे मुश्किल भी होती है. The सही ThingTM में आपको कई तरह की समस्याएं आती हैं. इसकी शुरुआत सामान्य चीज़ों से होती है, जैसे कि फ़ुटर में मौजूद लिंक, असल में पहुंच से बाहर होते हैं, क्योंकि कॉन्टेंट फ़ुटर को बार-बार भेजता है. हालांकि, समस्याएं बढ़ती गईं. जब कोई व्यक्ति अपने फ़ोन को पोर्ट्रेट से लैंडस्केप मोड में बदल देता है, तो साइज़ बदलने के इवेंट को कैसे हैंडल किया जाता है या सूची बहुत लंबी हो जाने पर फ़ोन को इसके रुकने से कैसे रोका जा सकता है?

सही चीज़TM

हमने सोचा कि यही वजह है कि हमने एक रेफ़रंस लागू किया, जिससे परफ़ॉर्मेंस के मानकों को बनाए रखते हुए, फिर से इस्तेमाल की जा सकने वाली इन सभी समस्याओं से निपटने का तरीका पता चले.

अपना लक्ष्य पूरा करने के लिए, हम तीन तकनीकों का इस्तेमाल करेंगे: डीओएम रीसाइकल करना, मकबरे बनाना, और स्क्रोल ऐंकरिंग.

हमारा डेमो केस Hangouts की तरह की चैट विंडो की तरह काम करने वाला है. इस विंडो में मैसेज को स्क्रोल किया जा सकता है. हमें सबसे पहले चैट मैसेज के अनंत स्रोत की ज़रूरत है. तकनीकी रूप से, इनमें से कोई भी इनफ़ाइनाइट स्क्रोलर असल नहीं होता है. हालांकि, इन स्क्रोलर में काम आने के लिए, जितना डेटा उपलब्ध होता है वह मौजूद होता है. आसानी के लिए, हम चैट मैसेज के एक सेट को हार्ड कोड करेंगे. साथ ही, मैसेज, लेखक, और कभी-कभी भेजे जाने वाले इमेज अटैचमेंट को भी चुनेंगे. इससे रीयल नेटवर्क की तरह काम करने में थोड़ा समय लग सकता है और थोड़ा इंतज़ार भी किया जा सकता है.

Chat ऐप्लिकेशन का स्क्रीनशॉट

डीओएम रीसाइकल करना

डीओएम नोड को कम रखने के लिए, डीओएम रीसाइकलिंग का कम इस्तेमाल होता है. सामान्य आइडिया यह है कि नए डीओएम एलिमेंट बनाने के बजाय, पहले से बने DOM एलिमेंट का इस्तेमाल किया जाए, जो स्क्रीन से बाहर के होते हैं. सच है, डीओएम नोड सस्ते होते हैं, लेकिन उन पर शुल्क नहीं लगता, क्योंकि उनमें से हर एक का इस्तेमाल करने से मेमोरी, लेआउट, स्टाइल, और पेंट में ज़्यादा खर्च आता है. अगर वेबसाइट के पास मैनेज करने के लिए बहुत बड़ा डीओएम है, तो सस्ते डिवाइस काफ़ी धीमे काम करेंगे. यह भी ध्यान रखें कि आपकी स्टाइल का हर रिले और दोबारा लागू होने वाली प्रोसेस, बड़े DOM के साथ ज़्यादा महंगा हो जाती है. यह एक ऐसी प्रोसेस है जो किसी नोड से क्लास को जोड़ने या हटाने पर ट्रिगर होती है. अपने डीओएम नोड को रीसाइकल करने का मतलब है कि हम डीओएम नोड की कुल संख्या को काफ़ी कम रखेंगे. इससे ये प्रोसेस ज़्यादा तेज़ हो जाएंगी.

सबसे पहली रुकावट खुद स्क्रोल करना है. हमारे पास किसी भी समय DOM में उपलब्ध सभी आइटम का एक छोटा सबसेट ही होता है, इसलिए हमें दूसरा तरीका ढूंढना होगा जिससे ब्राउज़र का स्क्रोलबार उस कॉन्टेंट को सही तरीके से दिखा सके जो सैद्धांतिक तौर पर वहां मौजूद है. हम 1px x 1px के सेटिनल एलिमेंट का इस्तेमाल करेंगे, जिसमें डेटा को सही ऊंचाई पर ले जाने के लिए, ट्रांसफ़ॉर्म ऐक्शन का इस्तेमाल किया जाएगा. हम रनवे में हर एलिमेंट को उनकी अपनी लेयर में प्रमोट करेंगे, ताकि रनवे की लेयर पूरी तरह से खाली हो सके. न कोई बैकग्राउंड रंग, कुछ भी नहीं. अगर रनवे की लेयर खाली नहीं है, तो यह ब्राउज़र के ऑप्टिमाइज़ेशन के लिए सही नहीं है. हमें अपने ग्राफ़िक कार्ड पर एक ऐसी टेक्सचर को स्टोर करना होगा जिसकी ऊंचाई हज़ार पिक्सल है. यह मोबाइल डिवाइस पर बिलकुल काम नहीं करता.

स्क्रोल करते समय, हम यह देखते हैं कि व्यूपोर्ट रनवे के आखिर में काफ़ी करीब आया है या नहीं. अगर ऐसा है, तो हम सेंटिनल एलिमेंट को दूसरी जगह ले जाकर रनवे का दायरा बढ़ा देंगे. इसके लिए, हम व्यूपोर्ट को रनवे में सबसे नीचे ले जाकर उन आइटम को नए कॉन्टेंट के साथ पॉप्यूलेट करेंगे.

रनवे भेजे गए भेजें

दूसरी दिशा में स्क्रोल करने पर भी यही बात लागू होती है. हालांकि, हम लागू करने के दौरान रनवे को कभी कम नहीं करेंगे, ताकि स्क्रोलबार की पोज़िशन एक जैसी बनी रहे.

टूंबस्टोन

जैसा कि हमने पहले बताया है, हम अपने डेटा सोर्स को असली दुनिया में मौजूद चीज़ों जैसा बनाने की कोशिश करते हैं. नेटवर्क के इंतज़ार के समय और हर चीज़ के साथ. इसका मतलब है कि अगर हमारे उपयोगकर्ता फ़्लिकी स्क्रोलिंग का इस्तेमाल करते हैं, तो वे आसानी से उस आखिरी एलिमेंट को स्क्रोल कर सकते हैं जिसके लिए हमारे पास डेटा है. अगर ऐसा होता है, तो हम मकबरे में बना एक प्लेसहोल्डर (प्लेसहोल्डर) वाला आइटम रखेंगे. डेटा आ जाने के बाद, इस आइटम की जगह असली कॉन्टेंट डाल दिया जाएगा. टूंबस्टोन को भी रीसाइकल किया जाता है और फिर से इस्तेमाल किए जा सकने वाले डीओएम एलिमेंट के लिए एक अलग पूल बनाया जाता है. हमें ऐसा इसलिए चाहिए, ताकि हम कब्र के पत्थर से कॉन्टेंट वाले आइटम में एक अच्छा बदलाव कर सकें. ऐसा न करने पर, लोगों को यह एहसास नहीं होता कि वे किस चीज़ पर फ़ोकस कर रहे हैं.

ऐसा मकबरा. बहुत पत्थर. वाह.

यहां एक दिलचस्प चुनौती यह है कि असली आइटम की ऊंचाई, मकबरे के आइटम से ज़्यादा हो सकती है, क्योंकि हर आइटम के लिए टेक्स्ट या अटैच की गई इमेज की मात्रा अलग-अलग होती है. इसे ठीक करने के लिए, हम हर बार डेटा आने पर स्क्रोल की मौजूदा स्थिति में बदलाव करेंगे और व्यूपोर्ट के ऊपर टॉमस्टोन को बदला जाएगा. इससे स्क्रोल की जगह को पिक्सल वैल्यू के बजाय किसी एलिमेंट पर ऐंकर किया जाएगा. इस सिद्धांत को स्क्रोल ऐंकरिंग कहा जाता है.

स्क्रोल ऐंकरिंग

कब्र के पत्थर बदलने के साथ-साथ, विंडो का साइज़ बदलने के साथ-साथ (यह तब भी होता है, जब डिवाइस को फ़्लिप किया जा रहा हो!) हमारी स्क्रोल ऐंकरिंग, दो तरीकों से इस्तेमाल की जाएगी. हमें यह पता लगाना होगा कि व्यूपोर्ट में सबसे ऊपर दिखने वाला एलिमेंट कौनसा है. वह एलिमेंट कुछ हद तक ही दिख सकता है, इसलिए हम एलिमेंट के ऊपर वाले हिस्से से ऑफ़सेट भी सेव करेंगे, जहां से व्यूपोर्ट शुरू होता है.

ऐंकरिंग डायग्राम को स्क्रोल करें.

अगर व्यूपोर्ट का साइज़ बदला जाता है और रनवे में बदलाव होते हैं, तो हम उस स्थिति को पहले जैसा कर सकते हैं जो दिखने में उपयोगकर्ता की तरह दिखती है. जीत! साइज़ वाली विंडो को छोड़कर, हर आइटम की लंबाई संभावित तौर पर बदल गई है, तो हम कैसे जान सकते हैं कि ऐंकर किए गए कॉन्टेंट को कितना नीचे रखा जाना चाहिए? हमारे पास नहीं है! यह पता लगाने के लिए हमें हर एलिमेंट को ऐंकर किए गए आइटम के ऊपर लेआउट बनाना होगा और उनकी पूरी ऊंचाई को जोड़ना होगा. इस वजह से, साइज़ बदलने के बाद, कॉन्टेंट में काफ़ी रुकावट आ सकती है और हम ऐसा नहीं चाहते. इसके बजाय, हम यह मानकर आगे बढ़ते हैं कि ऊपर दिए गए हर आइटम का साइज़, मकबरे के बराबर है और उसी हिसाब से स्क्रोल की स्थिति को अडजस्ट किया जाता है. एलिमेंट को रनवे पर स्क्रोल करते समय, हम स्क्रोल की पोज़िशन को अडजस्ट करते हैं. इसके लिए, हम लेआउट के काम को तब रोक देते हैं, जब असल में उसकी ज़रूरत होती है.

लेआउट

मैंने एक ज़रूरी जानकारी को स्किप कर दिया है: लेआउट. डीओएम एलिमेंट को हर बार रीसाइकल करने पर, आम तौर पर पूरे रनवे को रीसाइकल किया जाता है. इस वजह से, हम हर सेकंड में 60 फ़्रेम प्रति सेकंड के हमारे टारगेट से काफ़ी नीचे हो जाते हैं. इससे बचने के लिए, हम लेआउट का काम खुद कर रहे हैं और ट्रांसफ़ॉर्म के साथ बिलकुल पोज़िशन किए गए एलिमेंट का इस्तेमाल करते हैं. इस तरह हम दिखा सकते हैं कि रनवे पर आगे के सभी तत्व अब भी जगह ले रहे हैं, जबकि असल में सिर्फ़ खाली जगह मौजूद है. हम लेआउट खुद कर रहे हैं. इसलिए, हम उन पोज़िशन को कैश मेमोरी में सेव कर सकते हैं जहां हर आइटम पूरा होता है. साथ ही, जब उपयोगकर्ता पीछे की ओर स्क्रोल करता है, तब हम कैश मेमोरी से सही एलिमेंट को तुरंत लोड कर सकते हैं.

आम तौर पर, आइटम सिर्फ़ एक बार तब ही पेंट किया जाता है, जब वे डीओएम से अटैच हो जाते हैं और रनवे में दूसरे आइटम जोड़ या हटा देते हैं. ऐसा करना मुमकिन है, लेकिन सिर्फ़ मॉडर्न ब्राउज़र में.

ब्लीडिंग-एज में बदलाव

हाल ही में, Chrome ने सीएसएस कंटेनमेंट के लिए सहायता जोड़ी है. इस सुविधा की मदद से डेवलपर, ब्राउज़र को यह बता सकते हैं कि कोई एलिमेंट, लेआउट और पेंट की सीमा के बारे में बता सकता है. हम यहां लेआउट खुद कर रहे हैं. इसलिए, इस पर रोक लगाने का सबसे सही तरीका है. जब भी हम रनवे में कोई एलिमेंट जोड़ते हैं, तो हमें पता होता है कि दूसरे आइटम पर रिलेआउट का असर नहीं होना पड़ेगा. इसलिए, हर आइटम को contain: layout मिलना चाहिए. हम अपनी बाकी वेबसाइट पर भी असर नहीं डालना चाहते, इसलिए रनवे पर भी यह स्टाइल डायरेक्टिव भी मिलना चाहिए.

हमने IntersectionObservers को भी इस्तेमाल करने के तरीके का इस्तेमाल किया. इससे यह पता लगाया जा सकता है कि उपयोगकर्ता ने एलिमेंट को रीसाइकल करने और नया डेटा लोड करने के लिए, काफ़ी स्क्रोल किया है या नहीं. हालांकि, Intersection बेड को ज़्यादा इंतज़ार का समय (जैसे कि requestIdleCallback का इस्तेमाल करना) माना जाता है, इसलिए हो सकता है कि हम बिना Intersection निगरानी के बजाय रिस्पॉन्स करने के बजाय, मिलता कम हो. scroll इवेंट का इस्तेमाल करने के हमारे मौजूदा तरीकों में भी भी यही समस्या है. इसकी वजह यह है कि स्क्रोल इवेंट, “सबसे अच्छी कोशिश” के आधार पर भेजे जाते हैं. इस समस्या को हल करने के लिए, Houdini’s Composator Worklet पर सबसे सही तरीका है.

यह अब भी सटीक नहीं है

मौजूदा समय में डीओएम को रीसाइकल करना सही नहीं है, क्योंकि इसमें ऐसे सभी एलिमेंट जुड़ जाते हैं जो व्यूपोर्ट से पास होते हैं. इसके बजाय, हम स्क्रीन पर असल में मौजूद एलिमेंट पर ध्यान देते हैं. इसका मतलब है कि असल में तेज़ी से स्क्रोल करने पर, Chrome में लेआउट और पेंट पर इतना काम करना पड़ता है कि वह उसे पूरा नहीं कर पाता. आपको सिर्फ़ बैकग्राउंड के अलावा कुछ नहीं दिखेगा. यह दुनिया का अंत नहीं है, बल्कि इसमें कुछ और सुधार करना है.

हमें उम्मीद है कि जब आपको बेहतरीन उपयोगकर्ता अनुभव के साथ-साथ बेहतर परफ़ॉर्मेंस के स्टैंडर्ड के साथ-साथ मिलने वाली समस्याओं का भी सामना करना होगा, तो आपके लिए यह जानना आसान होगा कि समस्याएं कितनी मुश्किल हैं. प्रोग्रेसिव वेब ऐप्लिकेशन, मोबाइल फ़ोन के लिए मुख्य अनुभव बन रहे हैं. हालांकि, यह और ज़रूरी हो जाएगा. साथ ही, वेब डेवलपर को ऐसे पैटर्न का इस्तेमाल करते रहना होगा जो परफ़ॉर्मेंस की समस्याओं का सम्मान करते हों.

पूरा कोड हमारे डेटा स्टोर करने की जगह में मौजूद होता है. हमने इसे फिर से इस्तेमाल करने लायक बनाए रखने की पूरी कोशिश की है, लेकिन हम इसे एनपीएम पर या एक अलग डेटा स्टोर के तौर पर एक असल लाइब्रेरी के तौर पर पब्लिश नहीं करेंगे. इसका मुख्य इस्तेमाल शिक्षा देना है.