मार्विन फ्रैचेट द्वारा
खैर, यह साल React का साल लगता है। आपने शायद 16.7-alpha.0 के साथ आने वाले नए किलर फीचर - Hooks के बारे में सुना होगा। आपने शायद टाइम स्लाइसिंग या यहां तक कि Suspense जैसे कुछ अन्य महान और कूल स्टफ के बारे में भी सुना होगा।
यह लेख नहीं का उद्देश्य यह वर्णन करना है कि नई सुविधाओं में से कुछ का उपयोग कैसे करें, बल्कि यह साबित करना है कि उन्हें कैसे बनाया गया होगा। सिर्फ यह समझने के लिए कि हम किसके साथ खेल रहे हैं।
यह उस तरीके से भी लिखा गया है जिस तरह से मैंने इस सुविधा की खोज की। यह शायद वह तरीका नहीं है जिस तरह से इसके बारे में सोचा गया था, लेकिन इस तरह से मुझे अंक मिले।
पढ़ते समय आपको क्या मिलेगा:
- असिंक्रोनस JavaScript और इवेंट लूप
- React में बीजगणितीय प्रभाव, उदाहरण के साथ
- फाइबर और React चरण
मैंने यह पोस्ट क्यों लिखी?
जिस चीज ने मुझे यह पोस्ट लिखने के लिए प्रेरित किया वह यह विशेष, और प्रयोगात्मक, सुविधा थी जो असिंक्रोनस ऑपरेशंस का उपयोग सिंक्रोनस API के साथ करने की अनुमति देती है:
const bulbasaur = ApiResource.read()?… क्या? सिंक्रोनस?!
react-cache लाइब्रेरी असिंक्रोनस ऑपरेशंस का उपयोग सिंक्रोनस API के साथ करने की क्षमता बनाती है। यह वह सुविधा है जिसने मुझे यह सीखना चाहा कि React हुड के नीचे कैसे काम कर रहा है। यहां इस लाइब्रेरी पर डैन अब्रामोव और एंड्रयू क्लार्क द्वारा प्रस्तुत एक प्रस्तुति है:
यह कैसे संभव है? हम सिंक्रोनस कॉल्स का उपयोग करके रिमोट डेटा कैसे प्राप्त कर सकते हैं?
आइए इस उदाहरण में गहरा गोता लगाएं और यह समझने की कोशिश करें कि react-cache ऐसी कार्यक्षमता को कैसे लागू करता है और यह कैसे काम कर सकता है। यह कहानी फाइबर आर्किटेक्चर से शुरू होती है।
JavaScript ऑपरेशंस को नियंत्रित करना
फाइबर आर्किटेक्चर React को टास्क निष्पादन पर नियंत्रण रखने की अनुमति देता है। इसे उन कई समस्याओं को हल करने के लिए बनाया गया है जिनसे React पीड़ित था। यहां दो हैं जिन्होंने मेरा ध्यान खींचा:
- विशिष्ट घटनाओं को प्राथमिकता देना, जैसे डेटा फेचिंग पर उपयोगकर्ता इनपुट
- React गणना को असिंक्रोनस रूप से विभाजित करना ताकि मुख्य थ्रेड की उपलब्धता बनी रहे और लंबी रेंडरिंग प्रक्रियाओं के दौरान इसे ब्लॉक होने से बचाया जा सके
JavaScript एप्लिकेशन के अंदर स्टेट चेंज को ट्रिगर करने वाली हर चीज - न केवल React के साथ - असिंक्रोनस ऑपरेशंस के कारण होती है। इनमें setTimeout, fetch, और इवेंट्स के लिए लिस्नर्स शामिल हैं।
असिंक्रोनस ऑपरेशंस को कई JavaScript कोर अवधारणाओं के माध्यम से प्रबंधित किया जाता है:
- टास्क्स (माइक्रो, मैक्रो, रेंडर आदि...)
- इवेंट लूप
- कॉलस्टैक
यदि आप इन अवधारणाओं से परिचित नहीं हैं, तो मैं आपको जेक आर्किबाल्ड द्वारा इस वीडियो को देखने का सुझाव देता हूं:
फाइबर के लिए धन्यवाद, उपयोगकर्ता इनपुट हल हो जाते हैं अन्य असिंक्रोनस ऑपरेशंस जैसे फेच कॉल्स से पहले।
यह कैसे संभव है?
खैर, ऊपर आर्किबाल्ड की बात मेरे अपने सीखने के रास्ते का पहला पक्का पत्थर थी कि इवेंट लूप कैसे काम करता है। वह कहते हैं कि माइक्रो टास्क - उदाहरण के लिए Promise API के माध्यम से उत्पन्न - अगले मैक्रो टास्क से पहले निष्पादित और फ्लश किए जाते हैं। यह प्रक्रिया setTimeout जैसे कॉलबैक-आधारित तरीकों का उपयोग करती है।
तो, यदि आप मेरी "उपयोगकर्ता इनपुट बनाम डेटा फेचिंग" तुलना याद करते हैं, तो टीम ने fetch रिज़ॉल्यूशन को onChange रिज़ॉल्यूशन के बाद कैसे बनाया?
इनमें से कोई भी अवधारणा एक ही स्पेक में फिट नहीं होती, WhatWG / HTML5 / Ecma-262, और ब्राउज़र या JS इंजन जैसे विभिन्न स्थानों से प्रदान की जाती हैं।
मेरा मतलब है, हम Promise को setTimeout के बाद कैसे हल करने वाले हैं?
यह मुझे बिल्कुल पागल लगा और इस बात का अंदाजा लगाना वास्तव में कठिन था कि यह कैसे काम कर सकता है। तथ्य यह है कि यह एक उच्च स्तर पर होता है।
बाद में, मैंने React Rally में ब्रैंडन डेल की अविश्वसनीय बात देखी। यह नई टाइम स्लाइसिंग और Suspense सुविधाओं को प्रस्तुत करता है जिन्हें React फाइबर आर्किटेक्चर के लिए धन्यवाद दिया गया है:
डेल के अनुसार, फाइबर सामान्य JavaScript कॉलस्टैक की तरह है जहां स्टैक में प्रत्येक आइटम को फाइबर कहा जाता है। यह कॉलस्टैक से अलग है जो फ्रेम्स पर निर्भर करता है जो फंक्शन्स (+ मेटाडेटा) का प्रतिनिधित्व करते हैं। बल्कि, एक फाइबर एक कंपोनेंट (+ मेटाडेटा) का प्रतिनिधित्व करता है। एक फाइबर को एक कंपोनेंट के चारों ओर एक विशाल बॉक्स के रूप में देखें जो इसके बारे में सब कुछ जानता है।
इन दो अवधारणाओं के बीच एक महत्वपूर्ण अंतर है।
पहले हाथ पर, कॉलस्टैक एक कार्यक्षमता है जिसे देशी भाग ड्राइविंग JavaScript कोड के शीर्ष पर बनाया गया है। इसका उद्देश्य हर JavaScript फंक्शन कॉल को स्टैक करना और उन्हें अपने दम पर चलाना है। हर बार जब हम एक फंक्शन कॉल करते हैं तो इसे स्टैक में जोड़ा जाता है। कॉलस्टैक के बिना, हमारे पास स्पष्ट और विस्तृत त्रुटि स्टैकट्रेस नहीं होंगे। और चूंकि कॉलस्टैक JavaScript कोड से पहुंच योग्य नहीं है, इसलिए इस पर नियंत्रण रखना वास्तव में कठिन और यहां तक कि असंभव है।
दूसरे हाथ पर, फाइबर्स - जैसे फाइबर का एक स्टैक - उसी अवधारणा का प्रतिनिधित्व करते हैं, लेकिन JavaScript कोड में निर्मित। सबसे छोटी इकाई फंक्शन्स नहीं, बल्कि एक कंपोनेंटहै। यह वास्तव में एक JavaScript ब्रह्मांड में चलता है।
यह तथ्य कि फाइबर आर्किटेक्चर पूरी तरह से JavaScript में बनाया गया है, इसका मतलब है कि हम इसका उपयोग कर सकते हैं, इसे एक्सेस कर सकते हैं, और इसे संशोधित कर सकते हैं। हम मानक JavaScript का उपयोग करके इस पर काम कर सकते हैं।
विचार नहीं है कि JavaScript को कैसे काम करना चाहिए इसे पुनर्परिभाषित करें, जैसे यह बताना कि fetch माइक्रोटास्क रिज़ॉल्यूशन को कॉलबैक टास्क से पहले निष्पादित किया जाना चाहिए। यह इस पर अधिक है कि किस संदर्भ में React मेथड्स कहा जाए या नहीं, जैसे विभिन्न लाइफसाइकिल मेथड कॉल्स को बाधित करना।
अरे रुको! आप कहते हैं कि फाइबर्स React ऐप में सब कुछ नियंत्रित कर सकते हैं? लेकिन एक कंपोनेंट React को कुछ भी करना बंद करने के लिए कैसे कह सकता है?
बीजगणितीय प्रभाव, हां, लेकिन कृपया JavaScript में
React फाइबर आर्किटेक्चर की बदौलत कंपोनेंट्स को नियंत्रित करने और यह जानने में सक्षम है कि कोई कंपोनेंट चल रहा है या नहीं। अब जो गायब है वह React को यह बताने का एक तरीका है कि किसी विशिष्ट कंपोनेंट के लिए कुछ बदल गया है, ताकि वह इस परिवर्तन को संभाल सके।
यहीं बीजगणितीय प्रभाव खेल में आते हैं।
बीजगणितीय प्रभाव JavaScript में मौजूद नहीं हैं। मैं एक उच्च-स्तरीय व्याख्या के साथ यह समझाने की कोशिश करूंगा कि वे क्या हैं।
बीजगणितीय प्रभाव एक अवधारणा है जो कहीं जानकारी भेजने की अनुमति देती है, थोड़ा डिस्पैचर की तरह। विचार एक विशिष्ट फंक्शन को कॉल करना है जो वर्तमान में चल रहे फंक्शन को एक सटीक स्थिति पर बाधित करेगा ताकि एक पैरेंट फंक्शन एक गणना को संभाल सके। जब पैरेंट गणना समाप्त हो जाती है, तो यह प्रोग्राम को उस प्रारंभिक स्थिति पर फिर से शुरू कर सकता है जहां जानकारी भेजी गई थी।
OCaml या Eff जैसी कुछ भाषाएं इन सुविधाओं से मूल रूप से लाभान्वित होती हैं। यह वास्तव में एक दिलचस्प अमूर्त है क्योंकि कार्यान्वयन विवरण केवल पैरेंट पर निर्भर करते हैं:
क्या JavaScript में ऐसी सुविधा होना बहुत बढ़िया नहीं होगा?
React टीम ने JavaScript try/catch ब्लॉक से निपटने वाले React संदर्भ में एक समान दृष्टिकोण बनाया है। डेल के अनुसार, यह JavaScript में उपलब्ध निकटतम अवधारणा है।
कुछ फेंकना पैरेंट को कहीं जानकारी भेजने की अनुमति देता है। जानकारी को पकड़ने वाला पहला पैरेंट इससे निपटने और इस पर गणना करने में सक्षम है।
एक उदाहरण हजार शब्दों से बेहतर है
निम्नलिखित कोड की कल्पना करें जो सिंक्रोनस API का उपयोग करके Bulbasaur को फेच करने की कोशिश करता है:
यह कोड अजीब हो सकता है क्योंकि सिंक्रोनस API का उपयोग करके डेटा फेच करना वास्तव में आम नहीं है। आइए customFetch फंक्शन कार्यान्वयन के अंदर कूदें:
ओह रुको! यह बिल्कुल फेच जैसा नहीं दिखता! मुझे बिल्कुल समझ नहीं आ रहा कि यह फंक्शन क्या करना चाहता है…
खैर, कंपोनेंट के चारों ओर कुछ कल्पना करें, मान लीजिए एक फाइबर जो ऐसा दिखता है:
कोड को पढ़ने के लिए कुछ समय लें।
अब, आइए customFetch कार्यान्वयन पर जाएं:
पिछले स्निपेट्स में महत्वपूर्ण हिस्सा try/catch ब्लॉक है।
आइए इन अलग-अलग कोड टुकड़ों के माध्यम से क्या हो रहा है उसका सारांश लें:
PokemonकंपोनेंटcustomFetchमेथड को कॉल करता है।customFetchमेथड अपने आंतरिक कैश को पढ़ने की कोशिश करता है, लेकिन यह खाली है। तो यह कुछ फेंकता है / कहीं - बीजगणितीय प्रभाव।फाइबरपैरेंट उस जानकारी को पकड़ता है, इसे संभालता है, और डेटा फेच करता है। फिर यह डेटा के साथcustomFetchकैश को भरता है।Component(args)में एक री-रेंडर होता है और, अब,customFetchकैश भरा हुआ है। डेटा अब सिंक्रोनस API का उपयोग करके कंपोनेंट में उपलब्ध है।
विभिन्न थ्रो के लिए react-cache कार्यान्वयन विवरण देखें।
इस प्रक्रिया के दौरान कुछ आपका ध्यान आकर्षित कर सकता है: render को दो बार बुलाया गया है। एक त्रुटि फेंकने के लिए - कंपोनेंट को रोकना - और एक डेटा प्राप्त करने के लिए - कंपोनेंट को फिर से शुरू करना। React के साथ कई render कॉल्स को ट्रिगर करना ठीक है क्योंकि यह पूरी तरह से एक शुद्ध फंक्शन है - इसके अपने कोई साइड इफेक्ट्स नहीं हैं।
रुको... क्या? render के कोई साइड इफेक्ट्स नहीं हैं? DOM का क्या?
React चरण
यदि आप लंबे समय से React के साथ काम कर रहे हैं, तो आपने सुना होगा कि कई बार री-रेंडर करना अच्छी प्रथा नहीं है। फाइबर आर्किटेक्चर से पहले, हर बार जब हम रेंडर फंक्शन को कॉल करते थे तो React कुछ आंतरिक गणना करता था और फिर DOM को तदनुसार संशोधित करता था। उदाहरण के लिए, यह तब हुआ जब setState के माध्यम से रेंडर फंक्शन को कॉल किया गया। प्रक्रिया इनलाइन थी:
setState → render → वर्चुअल नोड्स की तुलना करें → DOM नोड्स अपडेट करें
फाइबर के साथ, प्रक्रिया थोड़ी अलग है। इसने एक कतार और बैच की अवधारणा पेश की है जो उच्च प्रदर्शन DOM संशोधनों की अनुमति देती है।
विचार काफी सरल है। हम मानते हैं कि स्क्रीन ~60 फ्रेम प्रति सेकंड चला सकती है। इस धारणा से, और उपलब्ध JavaScript फंक्शंस का उपयोग करके, हर ~16.7ms में कुछ गणनाएं और DOM संशोधन करना संभव है। फाइबर के साथ, React कई संशोधनों को कतारबद्ध कर सकता है और उन्हें प्रति सेकंड लगभग 60 बार कमिट कर सकता है।
इस तरह के संशोधन ने React को तीन चरणों में विभाजित करने की अनुमति दी है जिनके अपने फायदे और विशेषताएं हैं:
[डैन अब्रामोव React चरणों के बारे में](https://twitter.com/dan_abramov/status/981712092611989509/photo/1?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E981712092611989509&ref_url=https%3A%2F%2Fmedium.com%2Fmedia%2Fbda1c34a16e9f8a8e3eb244716a1da72%3FpostId%3D2a57dc9c2e6d)
- रेंडर चरण शुद्ध और निश्चित है। इसके कोई साइड इफेक्ट्स नहीं हैं और इसके द्वारा लिखे गए विभिन्न फंक्शन्स को कई बार बुलाया जा सकता है। रेंडर चरण बाधित करने योग्य है - यह
renderफंक्शन नहीं है जो पॉज मोड में है, बल्कि पूरा चरण - प्री-कमिट चरण का उद्देश्य वास्तविक DOM स्थिति तक पहुंच प्रदान करना है, जैसे स्क्रॉलबार पोजीशन, रीड मोड में।
- कमिट चरण वास्तव में DOM को संशोधित करता है और बाधित करने योग्य नहीं है। React उस चरण के दौरान रोक नहीं सकता।
चरणों के इस सेट ने टाइम स्लाइसिंग क्षमताओं को पेश किया है। React रेंडर चरण के दौरान, दो कंपोनेंट फंक्शन कॉल्स के बीच में रोक सकता है, और जब आवश्यक हो उस चरण को फिर से शुरू कर सकता है।
फाइबर में, render केवल कंपोनेंट के आंतरिक राज्य के आधार पर उसका नवीनतम उपलब्ध प्रतिनिधित्व प्राप्त करने का लक्ष्य रखता है ताकि कुछ तुलनाएं की जा सकें और पता लगाया जा सके कि React को DOM बदलना है या नहीं। यदि कमिट संशोधन की आवश्यकता है, तो यह संशोधन को "वर्क इन प्रोग्रेस" कतार में जोड़ देगा।
React टीम ने React Concurrent (टाइम स्लाइसिंग + Suspense) और फाइबर आर्किटेक्चर के लिए धन्यवाद बड़े प्रदर्शन सुधार किए हैं। उन्होंने इवेंट प्राथमिकता और कंकरेंसी जैसी विभिन्न ब्राउज़र समस्याओं का मुकाबला करने के लिए वर्कअराउंड बनाए हैं।
यदि हम एक कदम पीछे हटते हैं, तो क्या यह वही नहीं है जो उन्होंने दिखाया है? प्राथमिकता ब्राउज़र और फ्रंट-एंड फ्रेमवर्क के लिए नई चुनौती लगती है।
अन्य टीमें भी कला की वर्तमान स्थिति में सुधार पर काम कर रही हैं और यहां तक कि भविष्य के APIs का प्रस्ताव भी देती हैं। यह Google का दृष्टिकोण है: