बड़े JSON ट्री क्यों अटकते हैं: वर्चुअल स्क्रॉलिंग और कोलैप्स रणनीतियाँ
मुख्य बात: अटकन DOM नोड्स बनते समय शुरू होती है, स्क्रॉल करते समय नहीं। कोलैप्स-फ़र्स्ट रेंडरिंग, वर्चुअल सूचियाँ, सर्च हाइलाइटिंग और Worker सीमाएँ, मेट्रिक्स के साथ।

मुख्य निष्कर्ष: "धीमे" को पार्सिंग, नोड निर्माण, लेआउट और पेंट, तथा इंटरैक्शन में बाँटें, फिर हर चरण मापें। इसी क्रम में लागू करें — डिफ़ॉल्ट रूप से कोलैप्स, फिर वर्चुअल सूची जोड़ें, गणना को Worker में ले जाएँ, और हाइलाइटिंग को दृश्यमान पंक्तियों तक सीमित करें। कोलैप्स किए बिना सीधे वर्चुअल सूची पर कूदने से केवल दृश्यमान समूह छोटा होता है; पूरे ट्री की पुनर्गणना की लागत अब भी लगती है।
अटकन आम तौर पर उन 5MB के लिए बनाए गए लाखों DOM नोड्स से आती है, न कि स्वयं 5MB से।
1. "धीमे" को चार चरणों में बाँटें
जब कोई उपयोगकर्ता कहता है "यह खुलते ही फ़्रीज़ हो जाता है," समस्या चार बिल्कुल अलग चरणों में हो सकती है, हर एक का अलग इलाज है:
| चरण | सामान्य ट्रिगर | बोधगम्य सीमा | प्रमुख लागत |
|---|---|---|---|
| पार्स करें और ऑब्जेक्ट बनाएँ | फ़ाइल खोलना, टेक्स्ट पेस्ट करना | 50ms से अधिक कोई भी कार्य | स्ट्रिंग्स स्कैन करना, ऑब्जेक्ट ग्राफ़ बनाना |
| DOM नोड बनाएँ | पूरा ट्री एक साथ रेंडर करना | दसियों हज़ार नोड्स | एलिमेंट्स, टेक्स्ट नोड्स, स्टाइल ऑब्जेक्ट आवंटित करना |
| लेआउट और पेंट | पहला प्रदर्शन, स्क्रॉलिंग | 16.7ms से अधिक कोई भी फ़्रेम | ज्यामिति, रीफ़्लो, रीपेंट |
| इंटरैक्शन प्रतिक्रिया | एक्सपैंड, कोलैप्स, सर्च | 100ms से अधिक विलंबित लगता है | पुनः-समतलीकरण और पुनः-रेंडरिंग |
ब्राउज़र मुख्य थ्रेड एक समय में एक काम करता है। यदि कोई भी चरण इसे 50ms से अधिक घेरता है, तो स्क्रॉलिंग और टाइपिंग फ़्रेम गिराते हैं; ऐसे कई कार्य लगातार हों तो पेज मृत दिखता है। इसलिए पहला अनुकूलन requestAnimationFrame नहीं है; यह मापना है कि वास्तव में कौन-सा चरण समय खर्च कर रहा है।
2. नोड गिनती असली दुश्मन है
ट्री व्यूअर स्वभाव से नोड गिनती को गुणा करते हैं। एक JSON पंक्ति आम तौर पर "इंडेंट + कुंजी + कोलन + मान + कॉमा + शेवरॉन" बन जाती है, इसलिए रूढ़िवादी अनुमान प्रति पंक्ति 3 से 6 DOM नोड्स है:
| फ़ाइल आकार | अनुमानित पंक्तियाँ | ट्री-व्यू DOM नोड्स (अनुमान) | एक-बार रेंडरिंग कैसी दिखती है |
|---|---|---|---|
| 100 KB | 2,500 | 10,000 | आम तौर पर सहनीय |
| 1 MB | 25,000 | 100,000 | स्पष्ट रूप से जैंकी, संभवतः कई सेकंड की खाली स्क्रीन |
| 5 MB | 125,000 | 500,000 | बहुत संभव है अनुत्तरदायी |
| 20 MB | 500,000 | 2 मिलियन से अधिक | मेमोरी और लेआउट दोनों ध्वस्त |
हर एलिमेंट कुछ दर्जन बाइट के ऑब्जेक्ट से अधिक है: यह स्टाइल नियम, एक लेआउट बॉक्स, और पेंट निर्देश रखता है। बन जाने के बाद, कोई नोड अब भी लेआउट और स्टाइल का काम खर्च करता है, भले ही बाद में कोलैप्स किए गए मूल द्वारा छिपा दिया जाए, जब तक वह वास्तव में हटाया न गया हो। "सब कुछ रेंडर करो, फिर छिपा दो" अनुकूलन नहीं है; यह केवल लागत को अगले रीफ़्लो तक टालता है।
3. पहले कोलैप्स, फिर वर्चुअलाइज़
वर्चुअल स्क्रॉलिंग "बहुत अधिक दृश्यमान पंक्तियाँ" हल करता है। कोलैप्स "हमें इतनी पंक्तियाँ देखने की ज़रूरत ही नहीं होनी चाहिए" हल करता है। क्रम मायने रखता है: लाखों पंक्तियों वाले दस्तावेज़ के लिए जो डिफ़ॉल्ट रूप से विस्तृत है, उपयोगकर्ता के "सभी कोलैप्स करें" पर क्लिक करते ही एक आदर्श वर्चुअल सूची को भी पूरा ट्री पुनर्गणना करना पड़ता है।
एक व्यावहारिक कोलैप्स रणनीति:
- डिफ़ॉल्ट रूप से केवल पहले दो स्तर विस्तृत करें। रूट और उसके तत्काल फ़ील्ड दिखते हैं; गहरे स्तर
items: Array(12000)जैसा सारांश दिखाते हैं; - सारांश में प्रकार और गिनती डालें। "क्या फ़ील्ड नाम सही हैं" जाँचने वालों को शायद ही दस हज़ार रिकॉर्ड चाहिए;
- प्रति सबट्री पंक्तियाँ कैश करें। हर विस्तृत नोड अपनी समतल पंक्ति-सरणी रखता है; कोलैप्स पर उसे त्याग दें, और एक्सपैंड पर केवल उस सबट्री की पुनर्गणना करें;
- हर टॉगल पर पूरा ट्री कभी स्कैन न करें। यदि पंक्ति 3,000 पर विस्तार होता है, तो केवल उस रेंज को बदलें और आसपास की पंक्तियाँ अछूती रखें।
/**
* पूरे ट्री को पुनः समतल करने के बजाय किसी सबट्री की पंक्तियाँ उसके मूल के बाद डालें।
* @param rows वर्तमान दृश्यमान पंक्ति-सरणी
* @param parentIndex मूल पंक्ति का सूचकांक
* @param childRows सबट्री की समतल पंक्तियाँ
* @returns एक नई दृश्यमान पंक्ति-सरणी
*/
function replaceSubtreeRows(
rows: TreeRow[],
parentIndex: number,
childRows: TreeRow[],
): TreeRow[] {
const nextRows = rows.slice();
nextRows.splice(parentIndex + 1, 0, ...childRows);
return nextRows;
}
जिस पैटर्न से बचना है वह "एक एक्सपैंड, एक पूरा रीलेआउट" है। हर क्लिक पर पूरे ऑब्जेक्ट ग्राफ़ को फिर से चलाने से लागत दस्तावेज़ के साथ रैखिक रूप से बढ़ती है। प्रति-सबट्री कैशिंग के साथ, एक एक्सपैंड की लागत केवल उस सबट्री जितनी होती है।
4. वर्चुअल सूचियाँ: केवल व्यूपोर्ट रेंडर करें
एक बार कोलैप्स ने पंक्ति गिनती घटा दी, तो शेष अब भी दसियों हज़ार पंक्तियाँ हो सकती हैं। वर्चुअल सूची कंटेनर ऊँचाई को "कुल पंक्तियाँ x निश्चित पंक्ति ऊँचाई" पर सेट करती है और केवल व्यूपोर्ट तथा उसके ऊपर-नीचे थोड़ा बफ़र के लिए एलिमेंट बनाती है।
const ROW_HEIGHT = 24;
const OVERSCAN = 8;
/**
* उस पंक्ति-रेंज की गणना करें जिसे व्यूपोर्ट को रेंडर करना चाहिए।
* @param rows सभी दृश्यमान पंक्तियाँ
* @param scrollTop कंटेनर का वर्तमान स्क्रॉल ऑफ़सेट
* @param viewportHeight कंटेनर की दृश्यमान ऊँचाई
* @returns रेंडर करने की पंक्तियाँ, प्रारंभ सूचकांक, और लंबवत ऑफ़सेट
*/
function getViewportRows(
rows: TreeRow[],
scrollTop: number,
viewportHeight: number,
): { visibleRows: TreeRow[]; startIndex: number; offsetTop: number } {
const start = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
const end = Math.min(
rows.length,
Math.ceil((scrollTop + viewportHeight) / ROW_HEIGHT) + OVERSCAN,
);
const visibleNodes = rows.slice(start, end);
return {
visibleRows: visibleNodes,
startIndex: start,
offsetTop: start * ROW_HEIGHT,
};
}
अनिवार्य बाधाएँ:
- पंक्ति ऊँचाई निश्चित होनी चाहिए। तभी
scrollTop / ROW_HEIGHTआपको सीधे सूचकांक दे सकता है। लपेटी गई पंक्तियों की अनुमति देने से आपको हर ऊँचाई मापनी और कैश करनी पड़ती है, जो जटिलता और जिटर में परिमाण-कोटि जोड़ता है; transformया निरपेक्ष पोज़िशनिंग से ऑफ़सेट करें। स्पेसर एलिमेंट से सामग्री को नीचे न धकेलें, अन्यथा हर स्क्रॉल लेआउट बदलता है;- स्थिर कुंजियों का उपयोग करें। पंक्तियों को पथ से कुंजीबद्ध करें, ऐरे सूचकांक से नहीं, अन्यथा एक पंक्ति विस्तृत करने से उसके बाद की हर पंक्ति रीमाउंट होती है;
- स्क्रॉल हैंडलर में केवल अंकगणित करें। रेंडरिंग को
requestAnimationFrameपर टालें और दोहरावदार शेड्यूलिंग से बचाव करें; - बफ़र छोटा रखें। हर तरफ़ पाँच से दस पंक्तियाँ पर्याप्त हैं; बफ़र दोगुना करने से DOM नोड्स और स्टाइल का काम दोगुना होता है।
800-पिक्सेल व्यूपोर्ट और 24-पिक्सेल पंक्तियों के साथ, लगभग 34 पंक्तियाँ दिखती हैं, और बफ़र सहित भी आप लगभग पचास रेंडर करते हैं। अंतर्निहित दस्तावेज़ लाख पंक्तियाँ रख सकता है जबकि DOM लगातार कुछ सौ एलिमेंट रखता है, जिससे लेआउट लागत सपाट रहती है।
5. कोलैप्स, एक्सपैंड, और स्क्रॉल एंकरिंग
एक बार पंक्ति सूचकांक एकमात्र निर्देशांक बन जाता है, कुछ इंटरैक्शन विवरण टूटने लगते हैं:
- व्यूपोर्ट के ऊपर कोलैप्स करते समय दृश्य एंकर बनाए रखें। नीचे की पंक्तियाँ ऊपर खिसकती हैं; यदि
scrollTopको उसी कमिट में समायोजित न किया जाए, तो सामग्री कूदती दिखती है। एंकर पंक्ति का पथ दर्ज करें और उसके नए सूचकांक से स्क्रॉल स्थिति बहाल करें। - कूदने से पहले पूर्वजों को विस्तृत करें। जब सर्च किसी गहरे फ़ील्ड पर पहुँचता है, तो पहले हर मूल को विस्तृत करें, फिर लक्ष्य पंक्ति सूचकांक की गणना करें, फिर स्क्रॉल और हाइलाइट करें। पहला कदम छोड़ें और लक्ष्य पंक्ति दृश्यमान सरणी में ही नहीं होगी।
- कीबोर्ड नेविगेशन दृश्यमान पंक्तियों से चलाएँ। ऊपर और नीचे पंक्ति सूचकांक बदलते हैं; बाएँ और दाएँ विस्तृत और कोलैप्स करते हैं। किसी कुंजी हैंडलर के अंदर पूरा ट्री कभी न चलाएँ।
- अपडेट को एक फ़्रेम में बैच करें। लगातार कई स्तर विस्तृत करते समय, सरणी परिवर्तनों को मिलाएँ और एक बार रेंडर करें, अन्यथा हर स्तर पूरा रीफ़्लो ट्रिगर करता है।
6. सर्च और हाइलाइटिंग: एक बार गणना करें, थोड़ा प्रदर्शित करें
ट्री व्यूअर में सर्च सबसे अधिक अनदेखा किया जाने वाला प्रदर्शन जाल है। यदि हर कीस्ट्रोक पूरे ऑब्जेक्ट ग्राफ़ को चलता है और परिणाम सूची पुनः बनाता है, तो एक लाख-नोड वाला दस्तावेज़ तुरंत फ़्रीज़ हो जाएगा।
एक स्तरित दृष्टिकोण:
- इनपुट को 150-250ms तक डिबाउंस करें और हर सर्च को एक अनुक्रम संख्या से टैग करें ताकि पुराने परिणाम त्याग दिए जाएँ;
- Worker में स्कैन करें। ऑब्जेक्ट ग्राफ़ चलाना शुद्ध गणना है जिसका DOM पर कोई निर्भरता नहीं, इसलिए इसे टाइपिंग को अवरुद्ध नहीं करना चाहिए;
- केवल पथ और पंक्ति सूचकांक संग्रहित करें। हर मिले नोड की कॉपी या पहले से बना हाइलाइटेड HTML न बनाएँ;
- केवल दृश्यमान पंक्तियाँ हाइलाइट करें। व्यूपोर्ट में कुछ दर्जन पंक्तियाँ हैं; रेंडर समय पर मिले पथ से स्टाइल लागू करें;
- कूदने के लिए "पूर्वजों को विस्तृत करें, फिर सूचकांक खोजें" का पुनः उपयोग करें, वही पथ जो शेवरॉन पर क्लिक करने में उपयोग होता है।
एक सुरक्षा विवरण: ट्री व्यूअर में कुंजियाँ और मान उपयोगकर्ता डेटा हैं। उनसे हाइलाइटेड HTML बनाने के लिए एस्केपिंग चाहिए, अन्यथा मार्कअप वाली स्ट्रिंग XSS बन जाती है। JSON व्यूअर को रिच टेक्स्ट की ज़रूरत नहीं, इसलिए टेक्स्ट नोड्स प्लस क्लास नाम दोनों सुरक्षित और तेज़ हैं।
7. सिंटैक्स हाइलाइटिंग: CodeMirror और Monaco अलग उपकरण हैं
ट्री व्यू और टेक्स्ट एडिटर अलग-अलग रेंडरिंग मॉडल हैं, इसलिए "कौन तेज़ है" सीधी तुलना नहीं है:
| आयाम | CodeMirror 6 | Monaco Editor |
|---|---|---|
| स्थिति | मॉड्यूलर एडिटर कोर | पूर्ण IDE एडिटर |
| रेंडरिंग | व्यूपोर्ट-आधारित | व्यूपोर्ट-आधारित, लेकिन मॉडल और डेकोरेशन भारी |
| पार्सिंग | बदले गए रेंजों पर Lezer इंक्रीमेंटल पार्सिंग | Monarch टोकनाइज़र, पंक्ति-दर-पंक्ति |
| बड़ी फ़ाइलें | एक्सटेंशन पर निर्भर; डिफ़ॉल्ट रूप से हल्का | पूर्ण-सुविधा, अधिक मेमोरी और स्टार्टअप लागत |
| फ़ोल्डिंग और सर्च | अलग एक्सटेंशन, माँग पर लोड | अंतर्निहित, स्थायी ओवरहेड के साथ |
| सामान्य जाल | बहुत से एक्सटेंशन लागत बहाल कर देते हैं | Minimap, sticky scroll, और ढेर किए गए डेकोरेशन |
दो व्यावहारिक निष्कर्ष:
- ट्री व्यू को पूरा टोकनाइज़र चाहिए नहीं। कुंजियों, स्ट्रिंग्स, संख्याओं, और बूलियन को सादे टेक्स्ट नोड्स और CSS क्लासों के साथ एक-एक रंग दें; यह सिंटैक्स हाइलाइटिंग चलाने से परिमाण-कोटि कम खर्च करता है;
- ट्री व्यू और एडिटर एक साथ माउंट न करें। एक दस्तावेज़ के लिए दोनों मॉडल बनाए रखने से मेमोरी दोगुनी होती है; पुराने व्यू को छिपाने के बजाय नष्ट करें।
यदि एडिटर वाकई व्यूअर के अंदर चाहिए, तो CodeMirror 6 "केवल-पढ़ने, बड़ी फ़ाइल, स्थानीय हाइलाइटिंग" के लिए उपयुक्त है, जबकि Monaco कम्प्लीशन, डायग्नोस्टिक्स, और मल्टी-कर्सर संपादन के लिए। सुविधा सूची पढ़ने से पहले पहला-पेंट समय और निवासी मेमोरी मापें। एडिटर और CLI टूल की गहरी तुलना के लिए, देखें एडिटर, कमांड-लाइन टूल और ऑटोमेशन।
8. Worker सीमाएँ: पार्सिंग और पंक्ति निर्माण मुख्य थ्रेड से हटाएँ
रेंडरिंग जैंक का दूसरा आधा हिस्सा मुख्य थ्रेड पर पार्सिंग है। 1MB से अधिक JSON के लिए, पार्सिंग, फ़ॉर्मेटिंग, और समतल-पंक्ति निर्माण Web Worker में होना चाहिए; मुख्य थ्रेड केवल पंक्तियाँ लेता है और व्यूपोर्ट पेंट करता है।
Workers मुफ़्त नहीं हैं: postMessage structured clone का उपयोग करता है, इसलिए 50MB का ऑब्जेक्ट ग्राफ़ मुख्य थ्रेड को वापस भेजना दूसरी कॉपी बनाता है और शिखर दोगुना करता है। सही सीमा है Worker के अंदर पार्स और समतल करें, केवल वे पंक्तियाँ लौटाएँ जिनकी रेंडरर को ज़रूरत है:
worker.onmessage = (event: MessageEvent<{ rows: TreeRow[] }>) => {
rows = event.data.rows;
requestAnimationFrame(() => renderViewport());
};
worker.postMessage({ text });
यदि पंक्ति डेटा अब भी बड़ा है, तो पंक्तियों को समानांतर सरणियों (path, type, value offset) में संक्षिप्त करें, उन्हें ArrayBuffer में रखें, और स्वामित्व हस्तांतरित करें ताकि मुख्य थ्रेड कॉपी के लिए न चुकाए। लागत अपना सीरियलाइज़र लिखने की है, जो केवल लाखों पंक्तियों पर लायक है।
सीमा का नियम सरल है: मुख्य थ्रेड को केवल वह काम करना चाहिए जो DOM को छूना ही हो। पार्सिंग, सर्चिंग, सॉर्टिंग, फ़ॉर्मेटिंग, और डिफ़िंग सब हट सकते हैं। रेंडरिंग, स्क्रॉलिंग, इनपुट, और फ़ोकस मुख्य थ्रेड पर रहते हैं, और हर फ़्रेम को कार्य बजट में फ़िट होना चाहिए।
9. मेट्रिक्स और कार्यान्वयन क्रम
अनुकूलन "यह तेज़ लगता है" पर रुक नहीं सकता। निम्न समूह सीधे स्वीकृति चेकलिस्ट में जा सकता है:
| मेट्रिक | लक्ष्य | कैसे मापें |
|---|---|---|
| पहला दृश्यमान फ़्रेम | 300ms से नीचे | खोलने से पहले पेंट तक performance.now() |
| लंबे कार्य | 50ms से अधिक कोई नहीं | longtask देखता PerformanceObserver |
| स्क्रॉल फ़्रेम समय | लगातार 16.7ms से नीचे | DevTools Performance रिकॉर्डिंग |
| व्यूपोर्ट में DOM एलिमेंट | 2,000 से नीचे | document.querySelectorAll('*').length |
| एक्सपैंड / कोलैप्स प्रतिक्रिया | 50ms से नीचे | इंटरैक्शन के आसपास टाइमस्टैम्प |
| 100,000 पंक्तियों पर सर्च | 300ms से नीचे, Worker के अंदर | Worker में समय लें और अवधि लौटाएँ |
| निवासी हीप | टेक्स्ट आकार का 3 गुना से नीचे | DevTools Memory हीप स्नैपशॉट |
एकसमान रूप से उत्पन्न छोटे ऑब्जेक्ट्स के बजाय असली फ़ाइल से मापें; डेटा आकार मायने रखता है, और लंबी स्ट्रिंग्स, गहरी नेस्टिंग, और बहुत चौड़े ऐरे सभी अलग वक्र पैदा करते हैं। ऑब्जेक्ट-ग्राफ़ लेजर के लिए, देखें JSON मेमोरी लेजर।
इस क्रम में लागू करें:
- बेसलाइन मापें: पहला-रेंडर समय, नोड गिनती, स्क्रॉल फ़्रेम समय;
- डिफ़ॉल्ट रूप से कोलैप्स: दो स्तर विस्तृत करें, सारांश में प्रकार और गिनती दिखाएँ;
- वर्चुअल सूची: निश्चित पंक्ति ऊँचाई, स्थिर कुंजियाँ, व्यूपोर्ट प्लस छोटा बफ़र;
- गणना हटाएँ: पार्सिंग, सर्च, और समतलीकरण Worker में;
- हाइलाइटिंग सीमित करें: केवल दृश्यमान पंक्तियों को स्टाइल करें, पूरे ट्री को रीपेंट न करें;
- गार्डरेल जोड़ें: किसी सीमा से ऊपर, "पहली N पंक्तियाँ दिखाएँ" प्रदान करें या सर्च फ़िल्टर अनिवार्य करें।
हर कदम का एक ही लक्ष्य है: उन नोड्स और ऑब्जेक्ट्स को घटाना जिन्हें एक ही क्षण मौजूद रहना चाहिए, बजाय ब्राउज़र से उन पर और मेहनत कराने के। स्तरित रणनीति की पूरी पृष्ठभूमि बड़ी JSON का पार्सिंग, रेंडरिंग और स्ट्रीमिंग में है।
क्या वर्चुअल स्क्रॉलिंग पार्स-समय जैंक ठीक कर सकता है?
नहीं। वर्चुअल स्क्रॉलिंग केवल DOM नोड्स घटाता है। यदि पार्सिंग अब भी मुख्य थ्रेड पर हो रही है, तो पेज अब भी अटकेगा। 1MB से बड़े दस्तावेज़ों के लिए पार्सिंग Worker में ले जाएँ और दोनों समस्याओं को अलग-अलग संभालें।
सब कुछ विस्तृत है और फिर भी अटकता है। क्या ग़लत है?
या तो वर्चुअल सूची वास्तव में सक्रिय नहीं है, या हर एक्सपैंड पूरे ट्री को फिर स्कैन करता है। जाँचें कि ऑफ़-स्क्रीन पंक्तियाँ अब भी DOM बनाती हैं या नहीं, और कि एक्सपैंड केवल प्रभावित सबट्री की पुनर्गणना करता है या नहीं।
JSON व्यूअर के लिए CodeMirror या Monaco?
केवल-पढ़ने, बड़ी फ़ाइलों, और स्थानीय हाइलाइटिंग के लिए CodeMirror 6 पसंद करें; इसकी मॉड्यूलर, माँग-आधारित लोडिंग हल्की है। जब आपको कम्प्लीशन, डायग्नोस्टिक्स, और मल्टी-कर्सर संपादन चाहिए तो Monaco चुनें। किसी भी एक के साथ, एक ही दस्तावेज़ के लिए ट्री व्यू और एडिटर एक साथ माउंट करने से बचें।
सर्च जंप के बाद स्क्रॉल स्थिति ग़लत क्यों होती है?
आम तौर पर क्योंकि विस्तृत करने से पहले व्यू स्क्रॉल हो गया। पहले सभी पूर्वजों को विस्तृत करें, फिर नए पंक्ति सूचकांक से स्क्रॉल स्थिति सेट करें, और अंत में हाइलाइट करें। पंक्तियों को सूचकांक के बजाय पथ से कुंजीबद्ध करें ताकि विस्तार हर सूचकांक को न खिसकाए।
FAQ
मैं कैसे पता लगाऊँ कि अटकन किस चरण से आती है?
"धीमे" को पार्सिंग और ऑब्जेक्ट निर्माण, DOM नोड निर्माण, लेआउट और पेंट, तथा इंटरैक्शन प्रतिक्रिया में बाँटें, और खोलने से पहले पेंट तक performance.now() से हर चरण का समय लें। फिर PerformanceObserver से longtask देखें — 50ms से अधिक कोई भी कार्य फ़्रेम गिराता है, और DevTools Performance रिकॉर्डिंग दिखाती है कि स्क्रॉल फ़्रेम समय 16.7ms से नीचे रहता है या नहीं।
पूरा ट्री एक साथ रेंडर करने से स्क्रीन खाली क्यों हो जाती है?
5MB दस्तावेज़ लगभग 125,000 पंक्तियाँ है, और ट्री व्यू आम तौर पर हर पंक्ति को 3 से 6 DOM नोड्स में बदलता है — एक साथ लगभग 500,000 एलिमेंट। हर एलिमेंट स्टाइल नियम, एक लेआउट बॉक्स, और पेंट निर्देश भी रखता है, इसलिए 50ms से अधिक के कई मुख्य-थ्रेड कार्य लगातार पेज को मृत दिखाते हैं। कोलैप्स किए गए मूल द्वारा छिपे नोड्स लेआउट का काम अब भी खर्च करते हैं, इसलिए "सब कुछ रेंडर करो, फिर छिपा दो" अनुकूलन नहीं है।
मुझे Worker से 50MB का ऑब्जेक्ट ग्राफ़ वापस भेजना चाहिए?
नहीं। postMessage structured clone का उपयोग करता है, इसलिए प्राप्त पक्ष दूसरी कॉपी बनाता है और शिखर दोगुना होता है। सही सीमा है Worker के अंदर पार्स और समतल करना और केवल वे पंक्तियाँ लौटाना जिनकी रेंडरर को ज़रूरत है; लाखों पंक्तियों पर आप समानांतर सरणियों (path, type, value offset) को ArrayBuffer में पैक करके स्वामित्व हस्तांतरित कर सकते हैं।