كيفية جعل تطبيق React أسرع مع استضافة الدولة المشتركة

هذا المقال يدور حول ملوك الدولة ، أي عن الموقع المشترك للولايات ، ويمكن أيضًا ترجمة هذا المصطلح على أنه ملوك الدولة أو ملوك الدولة .


أحد الأسباب الرئيسية لإبطاء تطبيق React هو حالته العالمية. سأعرض هذا بمثال لتطبيق بسيط للغاية ، وبعد ذلك سأقدم مثالًا أقرب إلى الحياة الحقيقية.


إليك تطبيق بسيط يمكنك من خلاله إدخال اسم للكلب (إذا لم تعمل النافذة ، فإليك الرابط ):



إذا لعبت مع هذا التطبيق ، فستجد قريبًا أنه يعمل ببطء شديد. عند التفاعل مع أي مجال إدخال ، توجد مشاكل ملحوظة في الأداء. في مثل هذه الحالة ، يمكنك استخدام React.memo في شكل React.memo مع جميع المكونات مع تقديم بطيء. ولكن دعونا نحاول حل هذه المشكلة بشكل مختلف.


هنا هو رمز لهذا التطبيق:


 function sleep(time) { const done = Date.now() + time while (done > Date.now()) { // ... } } //      // -       function SlowComponent({time, onChange}) { sleep(time) return ( <div> Wow, that was{' '} <input value={time} type="number" onChange={e => onChange(Number(e.target.value))} /> ms slow </div> ) } function DogName({time, dog, onChange}) { return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => onChange(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } function App() { //   " " (global state) const [dog, setDog] = React.useState('') const [time, setTime] = React.useState(200) return ( <div> <DogName time={time} dog={dog} onChange={setDog} /> <SlowComponent time={time} onChange={setTime} /> </div> ) } 

إذا لم تكن قد قرأت المقال حول المملوك (الموقع المشترك) ، أقترح عليك قراءته. مع العلم أن الاستضافة المشتركة يمكن أن تجعل العمل مع تطبيقنا أسهل ، فلنستخدم هذا المبدأ عند العمل مع الدول.


انتبه إلى كود تطبيقنا ، أي حالة time - يتم استخدامه من قبل كل مكون من عناصر تطبيقنا ، وبالتالي تم رفعه ( رفع - رفع الحالة ) إلى مكون App (المكون الذي يلتف تطبيقنا بالكامل). ومع ذلك ، يتم استخدام حالة "dog" ( dog و setDog ) من قبل مكون واحد فقط ، وليس هناك حاجة إليها في مكون App ، لذلك دعونا DogName مكون DogName :


 function DogName({time}) { // <-    const [dog, setDog] = React.useState('') // <-   return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => setDog(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } function App() { //   " " (global state) const [time, setTime] = React.useState(200) return ( <div> <DogName time={time} /> // <-    <SlowComponent time={time} onChange={setTime} /> </div> ) } 

وإليكم نتائجنا (إذا لم تعمل النافذة ، فقم بالربط ):



نجاح باهر! إدخال اسم الآن يعمل بشكل أسرع بكثير. علاوة على ذلك ، أصبح الحفاظ على المكون أسهل بفضل الموقع المشترك. ولكن لماذا كان يعمل بشكل أسرع؟


يقولون إن أفضل طريقة لفعل شيء بسرعة هي القيام بأقل عدد ممكن من الأشياء. هذا هو بالضبط ما يحدث هنا. عندما ندير الحالة الموجودة أعلى شجرة المكون ، فإن كل تحديث لهذه الحالة سوف يبطل الشجرة بأكملها. رد الفعل لا يعرف ما الذي تغير ، ولهذا السبب ، عليه التحقق من جميع المكونات لمعرفة ما إذا كانت تحتاج إلى تحديثات DOM. هذه العملية ليست مجانية وتستهلك الموارد (خاصة إذا كان لديك مكونات بطيئة عن قصد). ولكن إذا قمت بنقل الحالة إلى أدنى مستوى ممكن في شجرة المكون ، كما فعلنا مع حالة dog DogName ، فإن DogName ستقوم بإجراء عمليات فحص أقل. لن يتحقق SlowComponent مكون SlowComponent (الذي SlowComponent بطيئًا عن عمد) ، لأن React يعرف أن هذا المكون لا يزال لا يمكن أن يؤثر على الإخراج.


باختصار ، في وقت سابق ، عند تغيير اسم الكلب ، تم فحص كل مكون بحثًا عن التغييرات (إعادة تقديمه). وبعد التغييرات التي أجريناها على الكود ، بدأ DogName في DogName مكون DogName فقط. وقد أدى هذا إلى زيادة ملحوظة في الإنتاجية!


في الحياة الحقيقية


أرى أن المطورين يضعون في مستودع Redux العالمي أو في السياق العالمي ما لا ينبغي أن يكون عالميًا. غالبًا ما تكون DogName مثل DogName من المثال أعلاه أماكن تحدث فيها مشكلة في الأداء. كثيرا ما أرى أن هذه المشكلة تتجلى عند التفاعل مع الماوس (على سبيل المثال ، عند عرض تلميح الأدوات أعلى الرسم البياني أو أعلى جدول البيانات).


أحد حلول هذه المشكلة هو "إلغاء" تفاعل المستخدم (أي ، ننتظر حتى يتوقف المستخدم عن الكتابة ، ثم نطبق تحديث الحالة). في بعض الأحيان ، هذا هو أفضل ما يمكننا فعله ، ولكنه قد يؤدي إلى تجربة سيئة للمستخدم (يجب أن يقلل الوضع المتزامن في المستقبل من الحاجة إلى القيام بذلك . راجع هذا العرض التوضيحي من Dan Abramov ).


الحل الآخر الذي يستخدمه المطورين غالبًا هو استخدام أحد عروض الإنقاذ React.memo ، على سبيل المثال React.memo . سيعمل هذا بشكل جيد للغاية في مثالنا بعيد المنال ، لأنه يتيح للرد SlowComponent تخطي إعادة تقديم SlowComponent ، ولكن من الناحية العملية قد يعاني التطبيق من "الوفاة من آلاف التخفيضات" ، لأنه في تطبيق حقيقي ، عادة ما يكون التباطؤ بسبب بطء واحد مكون ، وبسبب العمل السريع غير الكافي للعديد من المكونات ، لذلك يجب عليك استخدام React.memo كل مكان. بعد القيام بذلك ، سيكون عليك البدء في استخدام useMemo و useCallback ، وإلا فإن جميع الأعمال التي قمت بوضعها في React.memo سدى. يمكن أن تؤدي هذه الإجراءات إلى حل المشكلة ، ولكنها تزيد بشكل كبير من تعقيد التعليمات البرمجية الخاصة بك ، وفي الواقع ، لا تزال أقل فعالية من حالات المشاركة ، حيث إن React تحتاج إلى المرور عبر كل مكون (بدءًا من الأعلى) لتحديد ما إذا كان يجب تقديمه مرة أخرى.


إذا كنت تريد أن تتجول مع مثال أقل بقليل ، فانتقل إلى codeandbox هنا .


ما هو الموقع المشترك؟


ينص مبدأ التنسيب المشترك على:


يجب أن يكون الرمز في أقرب مكان ممكن من المكان الذي يتعلق به.

لذلك ، من أجل الامتثال لهذا المبدأ ، يجب أن تكون حالة dog داخل مكون DogName :


 function DogName({time}) { const [dog, setDog] = React.useState('') return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => setDog(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } 

ولكن ماذا لو قسمنا هذا المكون إلى عدة مكونات؟ أين يجب أن تذهب الدولة؟ الجواب هو نفسه: "أقرب ما يمكن من المكان الذي يتعلق به" ، وسيكون هذا هو أقرب مكون الأصل المشترك . كمثال ، دعنا DogName مكون DogName p عرض input و p في مكونات مختلفة:


 function DogName({time}) { const [dog, setDog] = React.useState('') return ( <div> <DogInput dog={dog} onChange={setDog} /> <DogFavoriteNumberDisplay time={time} dog={dog} /> </div> ) } function DogInput({dog, onChange}) { return ( <> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => onChange(e.target.value)} /> </> ) } function DogFavoriteNumberDisplay({time, dog}) { return ( <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> ) } 

لا يمكننا نقل الحالة إلى مكون DogInput ، لأن مكون DogFavoriteNumberDisplay يحتاج أيضًا إلى الوصول إلى الحالة ، لذلك ننتقل من أسفل إلى أعلى شجرة المكون حتى نجد الوالد المشترك لهذين المكونين وننظم إدارة الحالة فيه.


كل هذا ينطبق على المواقف التي تحتاج فيها إلى التحكم في حالة العشرات من المكونات على شاشة معينة من التطبيق الخاص بك. يمكنك حتى نقل هذا إلى السياق لتجنب حفر الدعامة إذا كنت تريد. لكن اجعل هذا السياق أقرب ما يكون إلى المكان الذي ينتمي إليه ، وبعد ذلك ستستمر في الحفاظ على الأداء الجيد (وسهولة استخدام الكود) الذي توفره المشاركة. تذكر أنك لا تحتاج إلى وضع جميع السياقات في المستوى الأعلى من تطبيق React الخاص بك. احتفظ بها حيث يكون أكثر منطقية.


هذه هي الفكرة الرئيسية لبلدي وظيفة أخرى ، إدارة حالة التطبيق مع رد الفعل . اجعل ولاياتك قريبة قدر الإمكان من المكان الذي يتم استخدامه فيه ، مما سيؤدي إلى تحسين أداء الشفرة وسهولة استخدامها. مع هذا النهج ، فإن الشيء الوحيد الذي قد يؤدي إلى تفاقم أداء التطبيق الخاص بك هو التفاعلات المعقدة بشكل خاص مع عناصر الواجهة.


فماذا تستخدم ، والسياقات أو الإعادة؟


إذا قرأت "خدعة واحدة بسيطة لتحسين إعادة عرض React" ، فأنت تعلم أنه يمكنك التأكد من تحديث المكونات التي تستخدم الحالة المتغيرة فقط. بهذه الطريقة يمكنك فقط حل هذه المشكلة. لكن لا يزال الناس يواجهون مشكلات في الأداء عند استخدام المحرر. تكمن المشكلة في أن React-Redux تتوقع منك اتباع توصياتها لتجنب التقديم غير الضروري للمكونات المتصلة . هناك دائمًا فرصة للخطأ ؛ يمكنك تكوين المكونات عن طريق الخطأ بحيث تبدأ في الظهور كثيرًا عندما تتغير الدول العالمية الأخرى. وكلما زاد تطبيقك ، كلما كان تأثير ذلك أكثر سلبية ، خاصةً إذا أضفت حالات كثيرة إلى المحرر.


هناك طرق يمكن أن تساعدك على تجنب هذه المشكلات ، على سبيل المثال ، استخدام memoized Reselect mapState ، أو قراءة وثائق المحررين للحصول على مزيد من المعلومات حول تحسين أداء التطبيق .


تجدر الإشارة إلى أنه يمكن تطبيق التعيين المشترك مع المحرر. مجرد استخدام المحررين فقط للدول العالمية ، ولكل شيء آخر ، استخدم الموقع المشترك. هناك بعض القواعد المفيدة في الأسئلة الشائعة للمحرر لمساعدتك في تحديد ما إذا كان يجب أن تعمل الحالة في "المحرر" أم يجب أن تظل في المكون .


بالمناسبة ، إذا قسمت ولايتك إلى مجالات (باستخدام عدة سياقات اعتمادًا على المجال) ، فستكون المشكلة أقل وضوحًا.


ولكن تظل الحقيقة هي أن الموقع المشترك للحالات يقلل من مشكلات الأداء ويبسط صيانة التعليمات البرمجية.


تقرر أين تضع الدولة


شجرة القرار:


، حيث وضع الدولة


نسخة نصية ، إذا لم يكن هناك طريقة لإلقاء نظرة على الصورة:


  • 1 نبدأ في تطوير التطبيق. انتقل إلى 2
  • 2 الحالة في المكون. اذهب إلى 3
  • 3 هل يستخدم الشرط فقط بواسطة هذا المكون؟
    • نعم؟ نذهب إلى 4
    • لا؟ هل تحتاج هذه الحالة إلى عنصر فرعي واحد فقط؟
    • نعم؟ انقله إلى هذا المكون الفرعي (استخدم الموقع المشترك). نذهب إلى 3.
    • لا؟ هل تحتاج هذه الحالة إلى مكونات الأصل أو المكونات "الأخوية" ، أي الأطفال من نفس المكون الأصل)؟
      • نعم؟ نقل الحالة أعلاه إلى المكون الأصل. اذهب إلى 3
      • لا؟ نذهب إلى 4
  • 4 اترك كما هو. اذهب إلى 5
  • 5 هل هناك مشكلة في حفر الدعامة؟
    • نعم؟ ننقل هذه الحالة إلى موفر السياق ونعرض هذا الموفر في المكون الذي تتم فيه إدارة الحالة. اذهب إلى 6
    • لا؟ اذهب إلى 6
  • 6 نرسل الطلب. عندما تظهر متطلبات جديدة ، انتقل إلى 1

من المهم أن تكون هذه العملية جزءًا من عملية إعادة هيكلة / صيانة طلبك المعتادة. إذا لم يتم رفع حالتك حيث يجب رفعها ، فستتوقف هذه الحالة عن العمل بشكل صحيح وستلاحظ ذلك. ومع ذلك ، إذا لم تتبع طريقة الموقع المشترك للولاية ، ولم تقم بخفض الحالات الأدنى في التسلسل الهرمي للمكونات ، فسيستمر تطبيقك في العمل. لهذا السبب ، لن تلاحظ على الفور مشاكل الأداء والإدارة التي ستتراكم تدريجياً.


استنتاج


بشكل عام ، يفهم المطورون جيدًا متى كان من الضروري رفع الحالة ("حالة الرفع") عندما يكون ذلك مطلوبًا ، لكننا لا نفهم جيدًا متى تحتاج الحالة إلى خفض. أقترح عليك إلقاء نظرة على رمز التطبيق الخاص بك والتفكير في المكان الذي يمكن حذف الدولة من خلال تطبيق مبدأ "الموقع المشترك". اسأل نفسك ، "هل أحتاج إلى حالة فتح نافذة مشروط في المحرر؟" (الجواب على الأرجح لا).


استخدم مبدأ الموقع المشترك ، وسوف يصبح كودك أسهل وأسرع.


حظا سعيدا

Source: https://habr.com/ru/post/ar485032/


All Articles