في هذه المقالة
- ما هو DOM؟
- كيف كانت تبنى تطبيقات الواجهات الأمامية قبل React؟
- كيف قام React بحل مشكلة الأداء؟
- مزايا أخرى في React
- الملخص
- المراجع
ما هو DOM؟
تمكنك لغة HTML من بناء واجهات ثابتة غير تفاعلية، فلو أردت مثلاً إخفاء عنصر عند الضغط على أحد الأزرار في الصفحة لن تتمكن من ذلك باستخدام HTML وحدها بل ستحتاج لـ Javascript.
لكن كيف يمكن لـ Javascript التخاطب مع صفحة نصية (صفحة HTML)؟
هنا يأتي دور الـ DOM وهو اختصار لـ Document Object Model. ولو قمنا بتقسيم الاسم لكلمات لكانت:
Document: أي صفحة الـ HTML. لا بد وأنك قرأت أو كتبت يوماً ما <!DOCTYPE html>
كأول سطر في صفحة HTML. أي أن صفحة HTML هي عبارة عن وثيقة (Document).
Object: هي تلك الكائنات التي تستخدمها في أي لغة برمجية غرضية التوجه وتحتوي على خصائص الكائن (Properties) وتوابعه (Methods).
Model: نموذج.
بالتالي النموذج الكائني لصفحة HTML.
عند فتح صفحة HTML على المتصفح، يقوم المتصفح بعمل نموذج (Model) من الكائنات (Objects) لصفحة HTML وهي الـ Document.
فمثلاً عنصر <html>
يقابله document
في DOM. وعنصر <body>
يقابلة document.body
ويمكنك جلب الكائنات المقابلة لأي عنصر في الصفحة عن طريق document.getElemenyById
أو document.querySelector
أو ما شابههما ثم استخدام خصائصها كـ width
و height
أو استخدام توابعها كما فعلها مع document.getElementById
هذا هو DOM. هو نفسها شجرة HTML لكن على شكل كائنات (Objects) يمكنك استخدامها في Javascript.
كيف كانت تبنى تطبيقات الواجهات الأمامية قبل React؟
قبل وجود React ومثيلاتها لم يكن من السهل بناء تطبيقات صفحة واحدة (Single page application). وكانت أغلب المواقع تستخدم jQuery للتفاعل مع صفحة HTML. و jQuery هذه هي توابع قصيرة وسهلة تقوم بتنفيذ أكواد DOM طويلة،
يعني تختصر على المبرمج كتابة ١٠ اسطر DOM بسطر واحد فقط مثلاً في jQuery.
لكن المشكلة ليست فقط في الاختصار على المبرمج. بل المشكلة الأكبر هي في الاختصار على المتصفح ومعالج الحاسوب. فإن عملية عرض الصفحة (Rendering) مكلفة جداً بالنسبة للمتصفح والمعالج وفي بعض الأحيان ولا بد أنها حصلت معك أن تتوقف صفحة HTML لفترة ثم تستعيد التجاوب وذلك بسبب عمل Javascript على Thread واحدة.
بالتالي لو كانت عملية العرض (Rendering) بطيئة فستتوقف صفحة HTML عن العمل لثوان مع كل تفاعل معها (مثلاً الضغط على زر يحذف عنصر).
قبل وجود التطبيقات وحيدة الصفحة (Single page application) كنت تقوم بتحميل الصفحة الجديدة وإعادة عرض (Render) لها في كل مرة تضغط على رابط جديد. حتى لو كانت الصفحة الجديدة تتشارك أغلب المكونات مع الصفحة القديمة.
ولا ننكر أن بعض التطبيقات كانت تحتوى على بعض ميزات التطبيقات وحيدة الصفحة (Single page application) لكن بشكل بسيط (مثل تطبيق مهام يقوم بتلوين مهمة باللون الأخضر لتعليمها كمنتهية عند الضغط عليها) من غير تحديث الصفحة كاملة. وكان يتم تغيير لون هذه المهمة أو وضع اشارة صح أمامها عن طريق DOM. لكن في الغالب ما كان يتم اعادة عرض جميع قائمة المهام مرة ثانية بدلاً من تحديث المهمة فقط وذلك للتخلص من التعقيد اللازم في العثور على المهمة المعدلة من بين قائمة المهام وتعديلها فقط.
وحتى لو كان يتم بناء تطبيقات وحيدة الصفحة قبل React ومثيلاتها فقد كانت معقدة جداً.
كيف قام React بحل مشكلة الأداء؟
قام React بعمل قفزة ثورية في تطبيقات الويب عن طريق تقديم مفهوم Virtual DOM.
اتفقنا أن التحديث على DOM عملية بطيئة ومكلفة بسبب عملية العرض (Render). لكن مثلاً مقارنة نصّين في Javascript أو كائنين هي عملية غير مكلفة أبداً مقارنةً بإعادة عرض كل الصفحة من جديد.
ما فعله React هو بناء نسخة وهمية من DOM ثم عند كل تفاعل مع الصفحة يقوم ببناء نسخة وهمية جديدة ومقارنتها مع النسخة السابقة ثم اكتشاف الأماكن التي تم تعديلها وتحديثها هي فقط في DOM الحقيقي.
تسمى هذه العملية Reconciliation.
لعمليه Reconciliation هذه خوارزميتان، قديمة ما قبل النسخة ١٦ من React وجديدة ما بعد النسخة ١٦.
الخوارزمية المعتمدة على المكدس (Stack reconciler):
في هذه الخوارزمية يتم إضافة كل تحديث جديد إلى مكدس التحديثات ثم تنفيذها واحدة تلو الأخرى. واجهت هذه الخوازمية عدة مشاكل منها:
- لا يمكن لـ React إعطاء أولوية لتحديث عن الآخر.
- لو تم تحديث لون عنصر ما مثلاً للول الأخضر ثم تحديثه للون الأحمر قبل عملية Reconciliation فسيقوم React بعرضه (Render) باللون الأخضر مرة ثم باللون الأحمر مرة أخرى، لكن في هذه الحالة كان يكفي عرضه بالحالة الحمراء لأنها الحالة النهائية.
- بما أن التحديثات تنفذ على Thread واحدة، فقد تحدث بعض التوقفات في الصفحة إن كان التحديث كبيراً أو لزمه حسابات كثيرة.
- التحديثات تتم واحدة تلو الأخرى ولذلك سيكون هناك وقت حتى يتم تنفيذ التحديث الأخير في المكدس.
خوارزمية (Fiber reconciler):
الهدف من هذه الخوارزمية هو جعل React قادراً على:
- تقسيم التحديث الواحد لتحديثات أصغر يمكن تنفيذها على عدة مراحل (حتى لا يتم ايقاف الواجهة).
- القدرة على إعطاء أولوية للتتحديثات
- القدرة على إيقاف التحديث الذي يتم تنفيذه الآن إن لم يكن مهماً (مثلاً الحالة الوسطى الخضراء في المثال أعلاه).
مزايا أخرى في React
التصميم المعتمد على إعادة استخدام العناصر (Component based architecture):
أتاح React القدرة على إعادة استخدام مكونات الصفحات المشتركة دون إعادة كتابة كودها كله وذلك عن طريق نظام العناصر (Components).
فمثلاً لو كنت تحتاج إلى زر يقوم بفتح أو إغلاق النافذة الجانبية الموجودة في عدة صفحات، يمكنك كتابة عنصر (Component) بهذا الشكل:
const ToggleSidebarButton = () => {
const handleToggleSidebar = () => {
// do some logic to hide sidebar here
};
return (
<button style={{ color: "red" }} onClick={handleToggleSidebar}>
{isSidebarHidden ? "Show sidebar" : "Hide sidebar"}
</button>
);
};
ثم يمكنك استخدامه في أي صفحة أخرى كما تستعمل أي عنصر HTML بهذا الشكل
<ToggleSidebarButton></ToggleSidebarButton>
JSX
تقوم في React بكتابة أكواد مشابهة لـ HTML وربطها مع الكود التفاعلي الخاص بها في Javascript في نفس الملف، مما يسهل عملية القراءة والتطوير.
كما رأينا في المثال أعلاه قمنا بكتابة التابع الذي يتفاعل مع الزر لفتح أو إغلاق النافذة الجانبية.
دعم تطبيقات الهاتف أو العرض من السرفر (Server Side Rendering):
إن عملية Reconciliation مفصولة تماماً عن Rendering في React ولذلك يمكنك تركيب أي Renderer على React لستستفيد من جميع مزاياه.
الملخص
تكمن قوة React في Virtual DOM وفي خوارزمية ايجاد فرق التغير على الواجهة أثناء التفاعل معها وكونه مكتبة بسيطة وسهلة التعلم وكذلك لا تقوم بإجبارك على هيكل ملفات معين (حتى لو كان تطبيقك صغيراً) كتلك المدعوة Angular.