Kodokon kodokon.com

रेंडरिंग पाइपलाइन: reflow, repaint, compositing

ब्राउज़र रेंडरिंग के चार चरणों को समझें ताकि आपको ठीक-ठीक पता चले कि कौन-सी CSS प्रॉपर्टीज़ महंगी हैं और will-change अंदरूनी तौर पर कैसे काम करता है।

11 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

ब्राउज़र आपकी CSS को चार-चरणों वाली पाइपलाइन के ज़रिए पिक्सेल में बदलता है: Style (वैल्यू की गणना), Layout (ज्यामिति, जिसे reflow भी कहते हैं), Paint (पिक्सेल में रैस्टराइज़ करना, यानी repaint) और Composite (GPU पर लेयर्स को जोड़ना)। सुनहरा नियम: आप जितने पहले के चरण को छूते हैं, बिल उतना ही बड़ा होता है, क्योंकि उसके बाद का हर चरण फिर से चलाया जाता है। width या top बदलने से layout + paint + composite ट्रिगर होता है। color या box-shadow बदलने से layout छूट जाता है पर repaint होता है। केवल transform और opacity को अकेले compositor thread द्वारा संभाला जा सकता है: एनिमेशन तब भी सहज रहता है जब मुख्य थ्रेड JavaScript से अवरुद्ध हो।

CSS
.slide-bad {
  position: absolute;
  transition: left 0.3s ease;
}
.slide-bad:hover { left: 120px; }

.slide-good {
  transition: transform 0.3s ease;
}
.slide-good:hover { transform: translateX(120px); }
दो दृष्टि से एक जैसे एनिमेशन: पहला हर फ़्रेम पर reflow को मजबूर करता है, दूसरा compositor पर बना रहता है।

ब्राउज़र आलसी होता है: यह आपके style राइट्स को बैच करता है और अगले रिफ़्रेश पर ही layout दोबारा गणना करता है। पर कुछ JavaScript रीड्स - offsetHeight, getBoundingClientRect(), scrollTop, किसी ज्यामितीय प्रॉपर्टी पर getComputedStyle() - अद्यतन वैल्यू की माँग करते हैं। अगर कोई राइट लंबित हो, तो ब्राउज़र को तुरंत और सिंक्रोनस रूप से दोबारा गणना करनी पड़ती है: यही एक forced reflow है। किसी लूप के भीतर रीड्स और राइट्स को बारी-बारी करने से layout thrashing होता है: पूरे बैच के लिए एक की जगह हर पुनरावृत्ति पर एक पूरा reflow।

JAVASCRIPT
const items = document.querySelectorAll('.item');

items.forEach((item) => {
  const h = item.offsetHeight;
  item.style.height = h * 2 + 'px';
});

const heights = [...items].map((i) => i.offsetHeight);
items.forEach((item, index) => {
  item.style.height = heights[index] * 2 + 'px';
});
संस्करण 1: हर एलिमेंट पर एक forced reflow। संस्करण 2: पहले सारे रीड्स, फिर सारे राइट्स - एक ही reflow।

Compositing पेज को लेयर्स में काटता है जिन्हें स्वतंत्र रूप से रैस्टराइज़ किया जाता है और GPU द्वारा जोड़ा जाता है। will-change: transform एनिमेशन से पहले एलिमेंट को उसकी अपनी लेयर में पदोन्नत करने का अनुरोध करता है, जिससे पहले फ़्रेम पर महंगी रैस्टराइज़ेशन से बचा जा सके। एक स्पेक बारीकी जो अक्सर अनदेखी रह जाती है: will-change को उसी प्रॉपर्टी जैसे साइड इफ़ेक्ट पैदा करने चाहिए जिसकी वह घोषणा करता है। इसलिए will-change: transform एक stacking context बनाता है और एलिमेंट को उसके position: fixed वंशजों के लिए एक containing block में बदल देता है - ठीक किसी असली transform की तरह। इसलिए एक साधारण परफ़ॉर्मेंस संकेत आपके stacking क्रम या आपके fixed एलिमेंट्स को तोड़ सकता है।

JAVASCRIPT
const panel = document.querySelector('.panel');

panel.addEventListener('pointerenter', () => {
  panel.style.willChange = 'transform';
});

panel.addEventListener('transitionend', () => {
  panel.style.willChange = 'auto';
});
स्पेसिफ़िकेशन द्वारा अनुशंसित पैटर्न: एनिमेशन से ठीक पहले पदोन्नत करें, ठीक बाद में रिलीज़ करें।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. कौन-सा एनिमेशन पूरी तरह compositor thread द्वारा संभाला जा सकता है, बिना किसी reflow या repaint के?
    • height 0 से 200px तक
    • margin-left 0 से 100px तक
    • transform: translateX(100px)
    • hover पर box-shadow
  2. एक ऐसे लूप के भीतर offsetHeight पढ़ना, जो styles को भी बदलता है, इतना महँगा क्यों होता है?
    • अगर कोई राइट लंबित हो तो हर रीड एक सिंक्रोनस reflow को मजबूर करता है
    • offsetHeight हर एक्सेस पर एक GPU repaint ट्रिगर करता है
    • यह प्रॉपर्टी JavaScript इंजन द्वारा कभी कैश नहीं की जाती
  3. will-change: transform किसी असली transform के साथ कौन-सा साइड इफ़ेक्ट साझा करता है?
    • यह एलिमेंट के CSS transitions को अक्षम कर देता है
    • यह एक stacking context और position: fixed के लिए एक containing block बनाता है
    • यह एलिमेंट को display: block में मजबूर करता है
    • यह रैस्टराइज़ेशन रिज़ॉल्यूशन को दोगुना कर देता है