रिएक्टच्या कामगिरीमागील जादू उघडा. ही सविस्तर मार्गदर्शिका रिकॉन्सिलिएशन अल्गोरिदम, व्हर्च्युअल DOM डिफिंग आणि मुख्य ऑप्टिमायझेशन धोरणे स्पष्ट करते.
रिएक्टचे गुप्त सूत्र: रिकॉन्सिलिएशन अल्गोरिदम आणि व्हर्च्युअल DOM डिफिंगचा सखोल अभ्यास
आधुनिक वेब डेव्हलपमेंटच्या जगात, रिएक्टने डायनॅमिक आणि इंटरॅक्टिव्ह यूजर इंटरफेस तयार करण्यासाठी स्वतःला एक प्रमुख शक्ती म्हणून स्थापित केले आहे. त्याची लोकप्रियता केवळ त्याच्या कंपोनेंट-आधारित आर्किटेक्चरमुळे नाही, तर त्याच्या उत्कृष्ट कामगिरीमुळेही आहे. पण रिएक्ट इतके वेगवान का आहे? याचे उत्तर जादू नाही; हे एक उत्कृष्ट अभियांत्रिकीचा नमुना आहे, ज्याला रिकॉन्सिलिएशन अल्गोरिदम म्हणून ओळखले जाते.
बऱ्याच डेव्हलपर्ससाठी, रिएक्टची अंतर्गत कार्यप्रणाली एक 'ब्लॅक बॉक्स' असते. आपण कंपोनेंट्स लिहितो, स्टेट मॅनेज करतो आणि UI अचूकपणे अपडेट होताना पाहतो. तथापि, या अखंड प्रक्रियेमागील यंत्रणा समजून घेणे, विशेषतः व्हर्च्युअल DOM आणि त्याचे डिफिंग अल्गोरिदम, हेच एका चांगल्या रिएक्ट डेव्हलपरला एका उत्कृष्ट डेव्हलपरपासून वेगळे करते. हे सखोल ज्ञान आपल्याला अत्यंत ऑप्टिमाइझ केलेले ॲप्लिकेशन्स लिहिण्यास, परफॉर्मन्सच्या समस्या शोधून त्या दूर करण्यास आणि लायब्ररीवर खऱ्या अर्थाने प्रभुत्व मिळवण्यास सक्षम करते.
ही सविस्तर मार्गदर्शिका रिएक्टच्या मूळ रेंडरिंग प्रक्रियेचे रहस्य उलगडेल. आपण डायरेक्ट DOM मॅनिप्युलेशन खर्चिक का आहे, व्हर्च्युअल DOM एक सुंदर उपाय कसा प्रदान करते, आणि रिकॉन्सिलिएशन अल्गोरिदम आपल्या UI ला कार्यक्षमतेने कसे अपडेट करते, याचा शोध घेऊ. आपण मूळ स्टॅक रिकन्सायलरपासून आधुनिक फायबर आर्किटेक्चरपर्यंतच्या प्रवासाचाही आढावा घेऊ आणि शेवटी, आपण आजच आपल्या ॲप्लिकेशन्सला ऑप्टिमाइझ करण्यासाठी वापरू शकता अशा कृतीयोग्य धोरणांसह निष्कर्ष काढू.
मूळ समस्या: डायरेक्ट DOM मॅनिप्युलेशन अकार्यक्षम का आहे
रिएक्टच्या उपायाचे कौतुक करण्यासाठी, आपल्याला प्रथम ते कोणती समस्या सोडवते हे समजून घेणे आवश्यक आहे. डॉक्युमेंट ऑब्जेक्ट मॉडेल (DOM) हे HTML डॉक्युमेंट्सचे प्रतिनिधित्व करण्यासाठी आणि त्यांच्याशी संवाद साधण्यासाठी एक ब्राउझर API आहे. हे ऑब्जेक्ट्सच्या ट्री (tree) संरचनेत असते, जिथे प्रत्येक नोड डॉक्युमेंटचा एक भाग दर्शवतो (जसे की एलिमेंट, टेक्स्ट किंवा ॲट्रिब्यूट).
जेव्हा तुम्हाला स्क्रीनवर काही बदलायचे असते, तेव्हा तुम्ही या DOM ट्रीमध्ये बदल करता. उदाहरणार्थ, नवीन लिस्ट आयटम जोडण्यासाठी, तुम्ही एक नवीन `
- ` नोडला जोडता. हे सरळ वाटत असले तरी, DOM ऑपरेशन्स संगणकीय दृष्ट्या खर्चिक असतात. त्याची कारणे खालीलप्रमाणे आहेत:
- लेआउट आणि रिफ्लो (Layout and Reflow): जेव्हा तुम्ही एखाद्या एलिमेंटची भूमिती बदलता (जसे की त्याची रुंदी, उंची किंवा स्थिती), तेव्हा ब्राउझरला सर्व प्रभावित एलिमेंट्सच्या स्थिती आणि परिमाणांची पुन्हा गणना करावी लागते. या प्रक्रियेला 'रिफ्लो' किंवा 'लेआउट' म्हणतात आणि ती संपूर्ण डॉक्युमेंटमध्ये पसरू शकते, ज्यामुळे मोठ्या प्रमाणात प्रोसेसिंग पॉवर वापरली जाते.
- रिपेंटिंग (Repainting): रिफ्लोनंतर, ब्राउझरला अपडेट केलेल्या एलिमेंट्ससाठी स्क्रीनवर पिक्सेल पुन्हा रेखांकित करावे लागतात. याला 'रिपेंटिंग' किंवा 'रास्टरायझिंग' म्हणतात. बॅकग्राउंड रंगासारखी एखादी साधी गोष्ट बदलल्यास कदाचित फक्त रिपेंट होऊ शकते, परंतु लेआउटमधील बदलामुळे नेहमीच रिपेंट होतो.
- सिंक्रोनस आणि ब्लॉकिंग (Synchronous and Blocking): DOM ऑपरेशन्स सिंक्रोनस असतात. जेव्हा तुमचा जावास्क्रिप्ट कोड DOM मध्ये बदल करतो, तेव्हा ब्राउझरला रिफ्लो आणि रिपेंट करण्यासाठी इतर कार्ये (वापरकर्त्याच्या इनपुटला प्रतिसाद देण्यासह) थांबवावी लागतात, ज्यामुळे यूजर इंटरफेस मंद किंवा फ्रीझ होऊ शकतो.
- प्रारंभिक रेंडर (Initial Render): जेव्हा तुमचे ॲप्लिकेशन पहिल्यांदा लोड होते, तेव्हा रिएक्ट तुमच्या UI साठी एक संपूर्ण व्हर्च्युअल DOM ट्री तयार करते आणि त्याचा वापर करून प्रारंभिक खरा DOM तयार करते.
- स्टेट अपडेट (State Update): जेव्हा ॲप्लिकेशनचे स्टेट बदलते (उदा. वापरकर्त्याने बटण क्लिक केल्यावर), तेव्हा रिएक्ट नवीन स्टेट दर्शवणारे एक नवीन व्हर्च्युअल DOM ट्री तयार करते.
- डिफिंग (Diffing): आता रिएक्टच्या मेमरीमध्ये दोन व्हर्च्युअल DOM ट्री असतात: जुने (स्टेट बदलण्यापूर्वीचे) आणि नवीन. त्यानंतर ते या दोन ट्रींची तुलना करण्यासाठी आणि त्यातील नेमके फरक ओळखण्यासाठी आपला 'डिफिंग' अल्गोरिदम चालवते.
- बॅचिंग आणि अपडेटिंग (Batching and Updating): नवीन व्हर्च्युअल DOM शी जुळण्यासाठी खऱ्या DOM ला अपडेट करण्यासाठी आवश्यक असलेल्या सर्वात कार्यक्षम आणि किमान ऑपरेशन्सची गणना रिएक्ट करते. ही ऑपरेशन्स एकत्र बॅच केली जातात आणि एकाच, ऑप्टिमाइझ केलेल्या क्रमाने खऱ्या DOM वर लागू केली जातात.
- ते संपूर्ण जुने ट्री मोडून टाकते, सर्व जुने कंपोनेंट्स अनमाउंट करते आणि त्यांचे स्टेट नष्ट करते.
- ते नवीन एलिमेंट प्रकारावर आधारित सुरवातीपासून एक पूर्णपणे नवीन ट्री तयार करते.
- आयटम B
- आयटम C
- आयटम A
- आयटम B
- आयटम C
- ते इंडेक्स 0 वरील जुन्या आयटमची ('आयटम B') इंडेक्स 0 वरील नवीन आयटमशी ('आयटम A') तुलना करते. ते वेगळे आहेत, म्हणून ते पहिल्या आयटममध्ये बदल करते.
- ते इंडेक्स 1 वरील जुन्या आयटमची ('आयटम C') इंडेक्स 1 वरील नवीन आयटमशी ('आयटम B') तुलना करते. ते वेगळे आहेत, म्हणून ते दुसऱ्या आयटममध्ये बदल करते.
- ते पाहते की इंडेक्स 2 वर एक नवीन आयटम ('आयटम C') आहे आणि तो इन्सर्ट करते.
- आयटम B
- आयटम C
- आयटम A
- आयटम B
- आयटम C
- रिएक्ट नवीन यादीतील चिल्ड्रेन पाहते आणि 'b' व 'c' कीज असलेले एलिमेंट्स शोधते.
- त्याला माहित आहे की 'b' आणि 'c' कीज असलेले एलिमेंट्स जुन्या यादीत आधीपासूनच अस्तित्वात आहेत, म्हणून ते त्यांना फक्त हलवते.
- ते पाहते की 'a' की असलेला एक नवीन एलिमेंट आहे जो पूर्वी अस्तित्वात नव्हता, म्हणून ते तयार करून इन्सर्ट करते.
- ... )`) एक अँटी-पॅटर्न आहे जर यादी कधीही पुनर्क्रमित (re-ordered), फिल्टर किंवा मधून आयटम जोडले/काढले जाऊ शकत असतील, कारण यामुळे की नसण्यासारख्याच समस्या निर्माण होतात. सर्वोत्तम कीज तुमच्या डेटामधील अद्वितीय आयडेंटिफायर्स असतात, जसे की डेटाबेस आयडी.
- इन्क्रिमेंटल रेंडरिंग (Incremental Rendering): हे रेंडरिंगचे काम लहान तुकड्यांमध्ये विभागू शकते आणि ते अनेक फ्रेम्सवर पसरवू शकते.
- प्राधान्यीकरण (Prioritization): हे विविध प्रकारच्या अपडेट्सना वेगवेगळे प्राधान्य स्तर देऊ शकते. उदाहरणार्थ, इनपुट फील्डमध्ये वापरकर्त्याच्या टायपिंगला पार्श्वभूमीत डेटा आणण्यापेक्षा जास्त प्राधान्य असते.
- थांबवण्याची आणि रद्द करण्याची क्षमता (Pausability and Abortability): हे उच्च-प्राधान्याच्या अपडेटला हाताळण्यासाठी कमी-प्राधान्याच्या अपडेटवरील काम थांबवू शकते, आणि यापुढे आवश्यक नसलेले काम रद्द करू शकते किंवा पुन्हा वापरू शकते.
- रेंडर/रिकॉन्सिलिएशन टप्पा (अस सिंक्रोनस): या टप्प्यात, रिएक्ट 'वर्क-इन-प्रोग्रेस' ट्री तयार करण्यासाठी फायबर नोड्सवर प्रक्रिया करते. ते कंपोनेंटचे `render` मेथड कॉल करते आणि DOM मध्ये काय बदल करणे आवश्यक आहे हे ठरवण्यासाठी डिफिंग अल्गोरिदम चालवते. महत्त्वाचे म्हणजे, हा टप्पा व्यत्यय आणण्यायोग्य (interruptible) आहे. रिएक्ट अधिक महत्त्वाचे काहीतरी हाताळण्यासाठी हे काम थांबवू शकते आणि नंतर पुन्हा सुरू करू शकते. यात व्यत्यय येऊ शकत असल्याने, असंगत UI स्थिती टाळण्यासाठी रिएक्ट या टप्प्यात कोणतेही वास्तविक DOM बदल लागू करत नाही.
- कमिट टप्पा (सिंक्रोनस): एकदा 'वर्क-इन-प्रोग्रेस' ट्री पूर्ण झाल्यावर, रिएक्ट कमिट टप्प्यात प्रवेश करते. ते गणना केलेले बदल घेते आणि ते खऱ्या DOM वर लागू करते. हा टप्पा सिंक्रोनस आहे आणि त्यात व्यत्यय आणला जाऊ शकत नाही. हे सुनिश्चित करते की वापरकर्त्याला नेहमी एक सुसंगत UI दिसेल. `componentDidMount` आणि `componentDidUpdate` सारखे लाइफसायकल मेथड्स, तसेच `useLayoutEffect` आणि `useEffect` हुक्स, या टप्प्यात कार्यान्वित केले जातात.
- `React.memo()`: फंक्शन कंपोनेंट्ससाठी एक हायर-ऑर्डर कंपोनेंट. हे कंपोनेंटच्या प्रॉप्सची शॅलो (shallow) तुलना करते. जर प्रॉप्स बदलले नाहीत, तर रिएक्ट कंपोनेंटला री-रेंडर करणे वगळेल आणि शेवटचा रेंडर केलेला परिणाम पुन्हा वापरेल.
- `useCallback()`: कंपोनेंटमध्ये परिभाषित केलेली फंक्शन्स प्रत्येक रेंडरवर पुन्हा तयार केली जातात. जर तुम्ही ही फंक्शन्स `React.memo` मध्ये गुंडाळलेल्या चाइल्ड कंपोनेंटला प्रॉप्स म्हणून पास केली, तर चाइल्ड री-रेंडर होईल कारण फंक्शन प्रॉप तांत्रिकदृष्ट्या प्रत्येक वेळी एक नवीन फंक्शन असते. `useCallback` फंक्शनलाच मेमोइझ (memoizes) करते, ज्यामुळे ते फक्त त्याच्या डिपेंडेंसीज बदलल्यासच पुन्हा तयार होते याची खात्री होते.
- `useMemo()`: `useCallback` प्रमाणेच, परंतु व्हॅल्यूजसाठी. हे एका खर्चिक गणनेच्या परिणामाला मेमोइझ करते. गणना फक्त तेव्हाच पुन्हा केली जाते जेव्हा त्याची एखादी डिपेंडेंसी बदलली असेल. हे प्रत्येक रेंडरवर खर्चिक गणना टाळण्यासाठी आणि प्रॉप्स म्हणून पास केलेल्या स्थिर ऑब्जेक्ट/ॲरे रेफरन्सेस टिकवून ठेवण्यासाठी उपयुक्त आहे.
हजारो नोड्स असलेल्या एका जटिल ॲप्लिकेशनची कल्पना करा. जर तुम्ही स्टेट अपडेट केले आणि थेट DOM मध्ये बदल करून संपूर्ण UI पुन्हा रेंडर केले, तर तुम्ही ब्राउझरला खर्चिक रिफ्लो आणि रिपेंटच्या मालिकेत ढकलत असाल, ज्यामुळे वापरकर्त्याचा अनुभव खूप वाईट होईल.
उपाय: व्हर्च्युअल DOM (VDOM)
रिएक्टच्या निर्मात्यांनी डायरेक्ट DOM मॅनिप्युलेशनमधील परफॉर्मन्सच्या समस्येला ओळखले. त्यांचा उपाय म्हणजे एक ॲबस्ट्रॅक्शन लेयर सादर करणे: व्हर्च्युअल DOM.
व्हर्च्युअल DOM म्हणजे काय?
व्हर्च्युअल DOM हे खऱ्या DOM चे एक हलके, इन-मेमरी प्रतिनिधित्व आहे. हे मूलतः एक साधा जावास्क्रिप्ट ऑब्जेक्ट आहे जो UI चे वर्णन करतो. VDOM ऑब्जेक्टमध्ये असे गुणधर्म असतात जे खऱ्या DOM एलिमेंटच्या ॲट्रिब्यूट्सचे प्रतिबिंब असतात. उदाहरणार्थ, एका साध्या `
{ type: 'div', props: { className: 'container', children: 'Hello World' } }
कारण हे फक्त जावास्क्रिप्ट ऑब्जेक्ट्स आहेत, त्यांना तयार करणे आणि त्यांच्यात बदल करणे खूप जलद असते. यात ब्राउझर API शी कोणताही संवाद होत नाही, त्यामुळे रिफ्लो किंवा रिपेंट होत नाहीत.
व्हर्च्युअल DOM कसे काम करते?
VDOM UI डेव्हलपमेंटसाठी डिक्लेरेटिव्ह (declarative) दृष्टिकोन सक्षम करते. ब्राउझरला कसे DOM बदलायचे हे टप्प्याटप्प्याने (imperative) सांगण्याऐवजी, तुम्ही फक्त दिलेल्या स्टेटसाठी UI कसे दिसले पाहिजे हे घोषित (declare) करता. बाकीचे काम रिएक्ट सांभाळते.
ही प्रक्रिया खालीलप्रमाणे दिसते:
अपडेट्स बॅच करून, रिएक्ट मंद DOM सोबतचा थेट संवाद कमी करते, ज्यामुळे परफॉर्मन्समध्ये लक्षणीय सुधारणा होते. या कार्यक्षमतेचे मूळ 'डिफिंग' टप्प्यात आहे, ज्याला औपचारिकपणे रिकॉन्सिलिएशन अल्गोरिदम म्हणून ओळखले जाते.
रिएक्टचे हृदय: रिकॉन्सिलिएशन अल्गोरिदम
रिकॉन्सिलिएशन ही अशी प्रक्रिया आहे ज्याद्वारे रिएक्ट नवीनतम कंपोनेंट ट्रीशी जुळण्यासाठी DOM अपडेट करते. ही तुलना करणार्या अल्गोरिदमला आपण 'डिफिंग अल्गोरिदम' म्हणतो.
सैद्धांतिकदृष्ट्या, एका ट्रीला दुसऱ्या ट्रीमध्ये रूपांतरित करण्यासाठी किमान बदलांची संख्या शोधणे ही एक अत्यंत गुंतागुंतीची समस्या आहे, ज्याची अल्गोरिदम कॉम्प्लेक्सिटी O(n³) च्या घरात आहे, जिथे n हे ट्रीमधील नोड्सची संख्या आहे. हे वास्तविक ॲप्लिकेशन्ससाठी खूपच मंद ठरले असते. ही समस्या सोडवण्यासाठी, रिएक्टच्या टीमने वेब ॲप्लिकेशन्स सामान्यतः कसे वागतात याबद्दल काही उत्कृष्ट निरीक्षणे केली आणि एक ह्युरिस्टिक (heuristic) अल्गोरिदम लागू केला जो खूप वेगवान आहे—O(n) वेळेत कार्यरत.
ह्युरिस्टिक्स: डिफिंगला वेगवान आणि अंदाजित बनवणे
रिएक्टचा डिफिंग अल्गोरिदम दोन प्राथमिक गृहीतकांवर किंवा ह्युरिस्टिक्सवर आधारित आहे:
ह्युरिस्टिक १: भिन्न एलिमेंट प्रकार भिन्न ट्री तयार करतात
हा पहिला आणि सर्वात सरळ नियम आहे. दोन VDOM नोड्सची तुलना करताना, रिएक्ट प्रथम त्यांचा प्रकार पाहते. जर रूट एलिमेंट्सचा प्रकार वेगळा असेल, तर रिएक्ट असे गृहीत धरते की डेव्हलपरला एकाला दुसऱ्यामध्ये रूपांतरित करण्याचा प्रयत्न करायचा नाही. त्याऐवजी, ते एक अधिक कठोर पण अंदाजित दृष्टिकोन घेते:
उदाहरणार्थ, हा बदल विचारात घ्या:
पूर्वी: <div><Counter /></div>
नंतर: <span><Counter /></span>
जरी चाइल्ड `Counter` कंपोनेंट समान असला तरी, रिएक्ट पाहते की रूट `div` वरून `span` मध्ये बदलला आहे. ते जुना `div` आणि त्यातील `Counter` इन्स्टन्स पूर्णपणे अनमाउंट करेल (त्याचे स्टेट गमावून) आणि नंतर एक नवीन `span` आणि `Counter` चा एक नवीन इन्स्टन्स माउंट करेल.
महत्वाचा मुद्दा: जर तुम्हाला एखाद्या कंपोनेंट सबट्रीचे स्टेट टिकवायचे असेल किंवा त्या सबट्रीचे पूर्ण री-रेंडर टाळायचे असेल, तर त्या सबट्रीच्या रूट एलिमेंटचा प्रकार बदलणे टाळा.
ह्युरिस्टिक २: डेव्हलपर्स `key` प्रॉपद्वारे स्थिर एलिमेंट्स सूचित करू शकतात
डेव्हलपर्ससाठी समजून घेण्यासाठी आणि योग्यरित्या लागू करण्यासाठी हे कदाचित सर्वात महत्त्वाचे ह्युरिस्टिक आहे. जेव्हा रिएक्ट चाइल्ड एलिमेंट्सच्या यादीची तुलना करते, तेव्हा त्याची डीफॉल्ट वर्तणूक दोन्ही याद्यांवर एकाच वेळी पुनरावृत्ती करणे आणि जिथे फरक असेल तिथे बदल (mutation) करणे ही असते.
इंडेक्स-आधारित डिफिंगची समस्या
चला कल्पना करूया की आपल्याकडे आयटम्सची एक यादी आहे आणि आपण `keys` न वापरता यादीच्या सुरुवातीला एक नवीन आयटम जोडतो.
प्रारंभिक यादी:
अपडेट केलेली यादी ('आयटम A' सुरुवातीला जोडून):
keys शिवाय, रिएक्ट एक साधी, इंडेक्स-आधारित तुलना करते:
हे अत्यंत अकार्यक्षम आहे. रिएक्टने दोन अनावश्यक बदल आणि एक इन्सर्शन केले आहे, जेव्हा फक्त सुरुवातीला एक इन्सर्शन करणे आवश्यक होते. जर हे लिस्ट आयटम्स स्वतःच्या स्टेटसह जटिल कंपोनेंट्स असते, तर यामुळे गंभीर परफॉर्मन्स समस्या आणि बग्स निर्माण होऊ शकतात, कारण कंपोनेंट्समध्ये स्टेटची सरमिसळ होऊ शकते.
`key` प्रॉपची शक्ती
`key` प्रॉप एक उपाय प्रदान करते. एलिमेंट्सच्या याद्या तयार करताना तुम्हाला समाविष्ट करणे आवश्यक असलेले हे एक विशेष स्ट्रिंग ॲट्रिब्यूट आहे. Keys प्रत्येक एलिमेंटला एक स्थिर ओळख देतात.
चला तेच उदाहरण पुन्हा पाहू, पण यावेळी स्थिर, अद्वितीय कीजसह:
प्रारंभिक यादी:
अपडेट केलेली यादी:
आता, रिएक्टची डिफिंग प्रक्रिया खूपच हुशार आहे:
हे खूपच कार्यक्षम आहे. रिएक्ट योग्यरित्या ओळखते की त्याला फक्त एक इन्सर्शन करण्याची आवश्यकता आहे. 'b' आणि 'c' कीजशी संबंधित कंपोनेंट्स जतन केले जातात, ज्यामुळे त्यांचे अंतर्गत स्टेट कायम राहते.
Keys साठी महत्त्वाचा नियम: Keys त्यांच्या सिबलिंग्जमध्ये (siblings) स्थिर, अंदाजित आणि अद्वितीय असणे आवश्यक आहे. ॲरे इंडेक्सचा की म्हणून वापर करणे (`items.map((item, index) =>
उत्क्रांती: स्टॅक ते फायबर आर्किटेक्चर
वर वर्णन केलेला रिकॉन्सिलिएशन अल्गोरिदम अनेक वर्षे रिएक्टचा पाया होता. तथापि, त्याची एक मोठी मर्यादा होती: तो सिंक्रोनस आणि ब्लॉकिंग होता. या मूळ अंमलबजावणीला आता स्टॅक रिकन्सायलर (Stack Reconciler) म्हणून ओळखले जाते.
जुनी पद्धत: स्टॅक रिकन्सायलर
स्टॅक रिकन्सायलरमध्ये, जेव्हा स्टेट अपडेटमुळे री-रेंडर ट्रिगर व्हायचे, तेव्हा रिएक्ट संपूर्ण कंपोनेंट ट्रीला रिकर्सिव्हली ट्रॅव्हर्स करायचे, बदल मोजायचे आणि ते DOM वर लागू करायचे - हे सर्व एकाच, अखंड क्रमाने व्हायचे. लहान अपडेट्ससाठी हे ठीक होते. परंतु मोठ्या कंपोनेंट ट्रीसाठी, या प्रक्रियेला बराच वेळ लागू शकत होता (उदा. १६ms पेक्षा जास्त), ज्यामुळे ब्राउझरचा मुख्य थ्रेड ब्लॉक व्हायचा. यामुळे UI प्रतिसादहीन व्हायचे, ज्यामुळे फ्रेम्स ड्रॉप होणे, जर्की ॲनिमेशन्स आणि वाईट वापरकर्ता अनुभव यासारख्या समस्या निर्माण व्हायच्या.
सादर आहे रिएक्ट फायबर (React 16+)
ही समस्या सोडवण्यासाठी, रिएक्ट टीमने मूळ रिकॉन्सिलिएशन अल्गोरिदम पूर्णपणे पुन्हा लिहिण्यासाठी एक बहु-वर्षीय प्रकल्प हाती घेतला. रिएक्ट १६ मध्ये रिलीज झालेले त्याचे परिणाम रिएक्ट फायबर (React Fiber) म्हणून ओळखले जाते.
फायबर आर्किटेक्चरची रचना मूळापासून कॉनकरन्सी (concurrency) सक्षम करण्यासाठी केली गेली होती—रिएक्टची एकाच वेळी अनेक कार्यांवर काम करण्याची आणि प्राधान्यानुसार त्यांच्यात स्विच करण्याची क्षमता.
'फायबर' हा एक साधा जावास्क्रिप्ट ऑब्जेक्ट आहे जो कामाच्या एका युनिटचे प्रतिनिधित्व करतो. त्यात एका कंपोनेंटबद्दल, त्याच्या इनपुट (props) आणि त्याच्या आउटपुट (children) बद्दल माहिती असते. व्यत्यय आणू न शकणाऱ्या रिकर्सिव्ह ट्रॅव्हर्सलऐवजी, रिएक्ट आता फायबर नोड्सच्या लिंक केलेल्या यादीवर एका वेळी एक प्रक्रिया करते.
या नवीन आर्किटेक्चरने अनेक महत्त्वाच्या क्षमता अनलॉक केल्या:
फायबरचे दोन टप्पे
फायबर अंतर्गत, रेंडरिंग प्रक्रिया दोन वेगळ्या टप्प्यांमध्ये विभागली जाते:
फायबर आर्किटेक्चर हे रिएक्टच्या अनेक आधुनिक वैशिष्ट्यांसाठी पाया आहे, ज्यात `Suspense`, कॉनकरंट रेंडरिंग, `useTransition`, आणि `useDeferredValue` यांचा समावेश आहे, जे सर्व डेव्हलपर्सना अधिक प्रतिसाद देणारे आणि प्रवाही यूजर इंटरफेस तयार करण्यास मदत करतात.
डेव्हलपर्ससाठी व्यावहारिक ऑप्टिमायझेशन धोरणे
रिएक्टची रिकॉन्सिलिएशन प्रक्रिया समजून घेतल्याने तुम्हाला अधिक कार्यक्षम कोड लिहिण्याची शक्ती मिळते. येथे काही कृतीयोग्य धोरणे आहेत:
१. याद्यांसाठी नेहमी स्थिर आणि अद्वितीय कीज वापरा
यावर पुरेसा जोर दिला जाऊ शकत नाही. याद्यांसाठी हे सर्वात महत्त्वाचे ऑप्टिमायझेशन आहे. तुमच्या डेटामधून एक अद्वितीय आयडी वापरा (उदा. `product.id`). ॲरे इंडेक्स वापरणे टाळा, जोपर्यंत यादी पूर्णपणे स्थिर नसेल आणि कधीही बदलणार नसेल.
२. अनावश्यक री-रेंडर्स टाळा
एखाद्या कंपोनेंटचे स्टेट बदलल्यास किंवा त्याचा पॅरेंट री-रेंडर झाल्यास तो कंपोनेंट री-रेंडर होतो. कधीकधी, आउटपुट समान असले तरीही कंपोनेंट री-रेंडर होतो. आपण हे वापरून टाळू शकता:
३. स्मार्ट कंपोनेंट कंपोझिशन
तुम्ही तुमच्या कंपोनेंट्सची रचना कशी करता याचा परफॉर्मन्सवर लक्षणीय परिणाम होऊ शकतो. जर तुमच्या कंपोनेंटच्या स्टेटचा एखादा भाग वारंवार अपडेट होत असेल, तर त्याला अपडेट न होणाऱ्या भागांपासून वेगळे करण्याचा प्रयत्न करा.
उदाहरणार्थ, एकाच मोठ्या कंपोनेंटमध्ये वारंवार बदलणारे इनपुट फील्ड ठेवण्याऐवजी, ज्यामुळे संपूर्ण कंपोनेंट री-रेंडर होतो, ते स्टेट त्याच्या स्वतःच्या लहान कंपोनेंटमध्ये ठेवा. अशा प्रकारे, जेव्हा वापरकर्ता टाइप करतो, तेव्हा फक्त तो लहान कंपोनेंट री-रेंडर होतो.
४. लांब याद्या व्हर्च्युअलाइझ करा
जर तुम्हाला शेकडो किंवा हजारो आयटम्स असलेल्या याद्या रेंडर करायच्या असतील, तर योग्य कीज वापरूनही, त्या सर्वांना एकाच वेळी रेंडर करणे मंद असू शकते आणि खूप मेमरी वापरू शकते. यावर उपाय म्हणजे व्हर्च्युअलायझेशन (virtualization) किंवा विंडोइंग (windowing). या तंत्रामध्ये फक्त तेच लहान आयटम्सचे सबसेट रेंडर करणे समाविष्ट आहे जे सध्या व्ह्यूपोर्टमध्ये दिसत आहेत. जसजसा वापरकर्ता स्क्रोल करतो, तसतसे जुने आयटम्स अनमाउंट होतात आणि नवीन आयटम्स माउंट होतात. `react-window` आणि `react-virtualized` सारख्या लायब्ररीज हे पॅटर्न लागू करण्यासाठी शक्तिशाली आणि वापरण्यास-सोपे कंपोनेंट्स प्रदान करतात.
निष्कर्ष
रिएक्टचा परफॉर्मन्स हा अपघात नाही; हे व्हर्च्युअल DOM आणि कार्यक्षम रिकॉन्सिलिएशन अल्गोरिदमवर केंद्रित असलेल्या एका विचारपूर्वक आणि अत्याधुनिक आर्किटेक्चरचा परिणाम आहे. डायरेक्ट DOM मॅनिप्युलेशनला दूर सारून, रिएक्ट अशा प्रकारे अपडेट्स बॅच आणि ऑप्टिमाइझ करू शकते जे मॅन्युअली व्यवस्थापित करणे खूपच गुंतागुंतीचे ठरले असते.
डेव्हलपर्स म्हणून, आपण या प्रक्रियेचा एक महत्त्वाचा भाग आहोत. डिफिंग अल्गोरिदमचे ह्युरिस्टिक्स समजून घेऊन—कीजचा योग्य वापर करून, कंपोनेंट्स आणि व्हॅल्यूज मेमोइझ करून, आणि आपल्या ॲप्लिकेशन्सची विचारपूर्वक रचना करून—आपण रिएक्टच्या रिकन्सायलरच्या विरुद्ध नाही, तर सोबत काम करू शकतो. फायबर आर्किटेक्चरच्या उत्क्रांतीने शक्यतेच्या सीमा आणखी पुढे ढकलल्या आहेत, ज्यामुळे प्रवाही आणि प्रतिसाद देणाऱ्या UIs च्या नवीन पिढीला सक्षम केले आहे.
पुढच्या वेळी जेव्हा तुम्ही स्टेट बदलानंतर तुमचा UI त्वरित अपडेट होताना पाहाल, तेव्हा पडद्याआड होणाऱ्या व्हर्च्युअल DOM, डिफिंग अल्गोरिदम आणि कमिट टप्प्याच्या सुंदर नृत्याचे कौतुक करण्यासाठी एक क्षण थांबा. ही समज तुम्हाला जागतिक प्रेक्षकांसाठी अधिक वेगवान, अधिक कार्यक्षम आणि अधिक मजबूत रिएक्ट ॲप्लिकेशन्स तयार करण्याची गुरुकिल्ली आहे.