المطابقة (Reconciliation)

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

تُزوّدنا React بواجهة برمجة تطبيقات (API) صريحة بحيث لا نقلق بشأن التغييرات التي تطرأ في كل تحديث. يجعل هذا من كتابة التطبيقات أمرًا أسهل بكثير، ولكن قد لا يكون من الواضح كثيرًا كيفيّة تطبيق هذا في React. تشرح هذه الصفحة الخيارات التي وضعناها في خوارزمية المقارنة (diffing) بحيث تكون تحديثات المُكوّنات متوقعة وفي نفس الوقت سريعة كفاية لأجل التطبيقات عالية الأداء.

البداية

عندما تستخدم React في نقطة زمنية محدّدة بإمكانك التفكير في التابع render()‎ كأنّه يُنشِئ شجرة من عناصر React، وعند التحديث التالي للخاصيّات props أو الحالة state سيُعيد التابع render()‎ شجرة مختلفة من عناصر React. تحتاج بعدها React لأن تعرف كيف ستُحدِّث واجهة المستخدم بكفاءة لُتطابِق آخر تحديث للشجرة.

هنالك بعض الحلول العامة لهذه المشكلة الحسابية لتوليد أقل عدد من العمليات المطلوبة للتحويل من شجرة إلى أخرى. على أية حال تمتلك حالة الخوارزميات تعقيدًا من الترتيب O(n^3)‎ حيث n هو عدد العناصر الموجودة في الشجرة.

إن استخدمنا هذا في React فسيتطلّب عرض 1000 عنصر من الأس (1) بليون مقارنة، وهذا مُكلِف جدًّا. تُنفِّذ React بدلًا من ذلك خوارزمية إرشاديّة O(n)‎ بناءً على افتراضين هما:

  1. سيُنتِج العنصران من نوعين مختلفين أشجار مختلفة.
  2. يُمكِن للمُطوّر أن يُلمِّح للعناصر الأبناء التي قد تكون مستقرة خلال تصييرات مختلفة عن طريق خاصيّة مفتاح (key) للإشارة إليها.

عمليًّا تكون هذه الافتراضات صحيحة تقريبًا لكل حالات الاستخدام العمليّة.

خوارزميّة المقارنة

عند مقارنة شجرتين تُقارِن React في البداية بين العنصرين الجذريين لهما. يختلف هذا السلوك اعتمادًا على أنواع العناصر الجذريّة.

العناصر من أنواع مختلفة

عندما يكون للعناصر الجذرية أنواع مختلفة تُجزِّء React الشجرة القديمة وتبني شجرة جديدة من الصفر، مُنطلِقةً من العنصر <a> إلى <img>، أو من العنصر <Article> إلى <Comment>، أو من العنصر <Button> إلى <div>، تُؤدّي أي من هذه العناصر إلى إعادة البناء بشكلٍ كامل.

عند تجزئة الشجرة تُدمَّر عُقَد DOM وتستقبل نُسَخ المُكوّنات التابع componentWillUnmount()‎. وعند بناء شجرة جديدة تُدخَل عُقَد DOM الجديدة ضمن DOM وتستقبل نُسَخ المُكوّنات التابع componentWillMount()‎ ثمّ التابع componentDidMount()‎، ونفقد أي حالة مرتبطة بالشجرة القديمة.

تتعرّض المُكوِّنات الموجودة تحت العنصر الجذري للفصل (unmount) وتدمير حالتها. على سبيل المثال عند إجراء خوارزمية المقارنة على الشيفرة التالية:

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

ستُدمِّر المُكوّن Counter القديم وتُعيد إنشاء واحد جديد.

عناصر DOM من نفس النوع

عند مقارنة عنصري DOM من نفس النوع، تبحث React في خاصيّاتهما وتبقي على نفس عُقدة DOM التحتية مع تحديث الخاصيّات المتغيّرة فقط، على سبيل المثال:

<div className="before" title="stuff" />

<div className="after" title="stuff" />

عن طريق مقارنة هذين العنصرين تعرف React أنّها يجب أن تُعدِّل فقط الخاصيّة className في عقدة DOM. عند تحديث الخاصيّة style تعرف React أنّها يجب أن تُحدِّث فقط الخاصيّات التي تغيّرت، على سبيل المثال:

<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

عند التحويل بين هذين العنصرين تعرف React أنّها يجب أن تُعدِّل التنسيق color وليس fontWeight.

بعد التعامل مع عقدة DOM تُكرِّر React نفس العمليّة للعناصر الأبناء.

عناصر المكونات من نفس النوع

عند تحديث المُكوّن تبقى نسخة المُكوّن على حالها من أجل الاحتفاظ بالحالة عبر التصييرات التالية. تُحدِّث React الخاصيّات props لنسخة المُكوّن لتُطابِق العنصر الجديد وتستدعي التوابع componentWillReceiveProps()‎ و componentWillUpdate()‎ في النسخة.

يُستدعى بعد ذلك التابع render()‎ وتتكرر خوارزمية المقارنة على النتيجة السابقة والنتيجة الجديدة.

التكرار على العناصر الأبناء

عند حدوث التكرار (Recursing) على العناصر الأبناء لعقدة DOM، تمر React افتراضيًّا عبر قائمتين للعناصر الأبناء بنفس الوقت وتُولِّد تغييرًا عندما تجد أي فرق.

على سبيل المثال عند إضافة عنصر في نهاية العناصر الأبناء يعمل التحويل بين هاتين الشجرتين بشكلٍ جيّد: