बड़े, जटिल लेआउट और लेआउट थ्रेशिंग से बचें

लेआउट वह जगह है जहां ब्राउज़र, एलिमेंट के लिए ज्यामितीय जानकारी का पता लगाता है - एलिमेंट का साइज़ और पेज में उनकी जगह. हर एलिमेंट के साइज़ की जानकारी साफ़ तौर पर या किसी और तरीके से दी जाएगी. यह जानकारी, इस्तेमाल किए गए सीएसएस, एलिमेंट के कॉन्टेंट या पैरंट एलिमेंट के आधार पर तय होती है. इस प्रोसेस को Chrome में लेआउट कहा जाता है.

लेआउट वह जगह है जहां ब्राउज़र, एलिमेंट के लिए ज्यामितीय जानकारी का पता लगाता है: पेज में उनका साइज़ और जगह. हर एलिमेंट के साइज़ की जानकारी साफ़ तौर पर या किसी और तरीके से दी जाएगी. यह जानकारी, इस्तेमाल किए गए सीएसएस, एलिमेंट के कॉन्टेंट या पैरंट एलिमेंट के आधार पर तय होती है. इस प्रोसेस को Chrome में लेआउट (और पहले से बनाए गए ब्राउज़र जैसे, Edge) और Safari में लेआउट कहा जाता है. Firefox में इसे रीफ़्लो कहा जाता है, लेकिन प्रोसेस असरदार तरीके से एक जैसी है.

स्टाइल कैलकुलेशन की तरह ही, लेआउट की लागत से जुड़ी ये समस्याएं हैं:

  1. ऐसे एलिमेंट की संख्या जिनके लिए लेआउट की ज़रूरत होती है. यह लेआउट, पेज के DOM साइज़ का उप-प्रॉडक्ट होता है.
  2. उन लेआउट की जटिलता.

खास जानकारी

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

इंटरैक्शन के इंतज़ार में लगने वाले समय पर लेआउट का असर

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

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

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

जहां भी हो सके, लेआउट का इस्तेमाल करने से बचें

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

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

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

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

DevTools, लेआउट में लंबे समय से दिख रहा है.

ऊपर दिए गए उदाहरण में ट्रेस की पड़ताल करने पर, हमने देखा कि हर फ़्रेम के लिए लेआउट के अंदर 28 मिलीसेकंड से ज़्यादा समय खर्च होता है, जब किसी ऐनिमेशन में स्क्रीन पर फ़्रेम पाने के लिए 16 मिलीसेकंड होते हैं, तो वह काफ़ी ज़्यादा होता है. यह भी देखा जा सकता है कि DevTools आपको ट्री साइज़ की जानकारी देगा (इस मामले में 1,618 एलिमेंट). साथ ही, यह भी पता चलेगा कि आपको कितने नोड को लेआउट की ज़रूरत थी (इस मामले में 5 नंबर).

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

फ़ोर्स किए गए सिंक्रोनस लेआउट से बचें

स्क्रीन के लिए फ़्रेम शिप करने का यह क्रम है:

लेआउट के तौर पर flexbox का इस्तेमाल करना.

पहले JavaScript चलाया जाता है, फिर स्टाइल कैलकुलेशन फिर लेआउट चलाना. हालांकि, ब्राउज़र को JavaScript का इस्तेमाल करके, पहले लेआउट का इस्तेमाल करने के लिए मजबूर किया जा सकता है. इसे फ़ोर्स्ड सिंक्रोनस लेआउट कहा जाता है.

सबसे पहले ध्यान में रखें कि JavaScript चलाने के बाद पिछले फ़्रेम से सभी पुराने लेआउट वैल्यू जानी-पहचानी होती हैं और आपके लिए क्वेरी करने के लिए उपलब्ध होते हैं. इसलिए, अगर उदाहरण के लिए, आप फ़्रेम की शुरुआत में किसी एलिमेंट की ऊंचाई लिखना चाहते हैं (इसे "बॉक्स" कहते हैं), तो आप इस तरह के कुछ कोड लिख सकते हैं:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

अगर आपने बॉक्स की ऊंचाई मांगने से पहले उसकी स्टाइल बदल दी है, तो समस्या का सामना करना पड़ सकता है:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

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

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

ऊपर दिया गया फ़ंक्शन सही तरीके से हो जाएगा:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

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

लेआउट थ्रैशिंग से बचें

फ़ोर्स किए गए सिंक्रोनस लेआउट को और भी खराब बनाने का एक तरीका है: उन्हें तुरंत एक के बाद एक करें. इस कोड को देखें:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

यह कोड, पैराग्राफ़ के ग्रुप पर लूप करता है. साथ ही, हर पैराग्राफ़ की चौड़ाई को "box" नाम के एलिमेंट की चौड़ाई से मैच करने के लिए सेट करता है. इससे कोई नुकसान नहीं होता है, लेकिन समस्या यह है कि लूप का हर इटरेशन, स्टाइल वैल्यू (box.offsetWidth) को पढ़ता है और फिर पैराग्राफ़ की चौड़ाई (paragraphs[i].style.width) को अपडेट करने के लिए तुरंत इसका इस्तेमाल करता है. लूप के अगले दोहराव पर, ब्राउज़र को यह ध्यान रखना होगा कि offsetWidth के पिछली बार अनुरोध किए जाने के बाद से स्टाइल में बदलाव हुए हैं. इसलिए, इसे स्टाइल में बदलाव लागू करना होगा और लेआउट को चलाना होगा. यह दोहराव पर होगा.

इस सैंपल को ठीक करने के लिए, वैल्यू को एक बार फिर से पढ़कर और लिखना होगा:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

अगर आपको सुरक्षा की गारंटी देनी है, तो FastDOM का इस्तेमाल करें. यह आपके लिए, पढ़े जाने और लिखने के लिए अपने-आप बैच बनाता है. साथ ही, यह आपको गलती से फ़ोर्स किए गए सिंक्रोनस लेआउट या लेआउट थ्रैशिंग को ट्रिगर करने से रोकता है.

अनस्प्लैश चैनल की हीरो इमेज, जिसे हैल गेटवुड ने लिखा है.