ما تحله أطر الويب وكيف تفعل بدونها (الجزء 1)
نشرت: 2022-03-10لقد أصبحت مؤخرًا مهتمًا جدًا بمقارنة الأطر مع Vanilla JavaScript. بدأ الأمر بعد بعض الإحباط الذي كنت أستخدمه في React في بعض مشاريع العمل المستقل ، ومع معرفتي الأخيرة والأكثر حميمية بمعايير الويب كمحرر للمواصفات.
كنت مهتمًا بمعرفة ما هي القواسم المشتركة والاختلافات بين الأطر ، وما يجب أن تقدمه منصة الويب كبديل أصغر حجمًا ، وما إذا كانت كافية. هدفي ليس سحق الأطر ، بل فهم التكاليف والفوائد ، وتحديد ما إذا كان البديل موجودًا ، ومعرفة ما إذا كان بإمكاننا التعلم منه ، حتى لو قررنا استخدام إطار عمل.
في هذا الجزء الأول ، سأتعمق في بعض الميزات الفنية المشتركة عبر الأطر ، وكيف تقوم بعض الأطر المختلفة بتنفيذها. سأنظر أيضًا في تكلفة استخدام تلك الأطر.
الأطر
اخترت أربعة أطر عمل للنظر فيها: React ، كونها المهيمنة اليوم ، وثلاثة متنافسين جدد يدعون القيام بالأشياء بشكل مختلف عن React.
- تتفاعل
"تجعل React إنشاء واجهات مستخدم تفاعلية أمرًا غير مؤلم. تجعل العروض التقريرية شفرتك أكثر قابلية للتنبؤ وأسهل في التصحيح ". - SolidJS
"يتبع Solid نفس فلسفة React ... ومع ذلك فإن له تطبيقًا مختلفًا تمامًا يتجاهل استخدام DOM الظاهري." - ممشوق
"Svelte هو نهج جديد جذري لبناء واجهات مستخدم ... خطوة تجميع تحدث عند إنشاء تطبيقك. بدلاً من استخدام تقنيات مثل اختلاف DOM الافتراضي ، يكتب Svelte رمزًا يقوم بتحديث DOM جراحيًا عندما تتغير حالة تطبيقك. " - أشعل
"بناءً على معايير مكونات الويب ، يضيف Lit فقط… التفاعلية ، والقوالب التوضيحية ، وحفنة من الميزات المدروسة."
لتلخيص ما تقوله الأطر عن مفارقاتها:
- تجعل React بناء واجهات المستخدم أسهل باستخدام طرق العرض التقريرية.
- تتبع SolidJS فلسفة React لكنها تستخدم أسلوبًا مختلفًا.
- يستخدم Svelte نهج وقت الترجمة لواجهات المستخدم.
- يستخدم Lit المعايير الحالية ، مع بعض الميزات خفيفة الوزن المضافة.
ما الأطر حل
تذكر الأطر نفسها الكلمات التصريحية والتفاعلية والظاهرية. دعونا نتعمق في ما يعنيه هؤلاء.
البرمجة التصريحية
البرمجة التعريفية هي نموذج يتم فيه تعريف المنطق دون تحديد تدفق التحكم. نحن نصف ما يجب أن تكون عليه النتيجة ، وليس الخطوات التي ستأخذنا إلى هناك.
في الأيام الأولى من الأطر التعريفية ، حوالي عام 2010 ، كانت واجهات برمجة تطبيقات DOM أكثر وضوحًا وإسهابًا ، وتطلبت كتابة تطبيقات الويب باستخدام JavaScript الضروري الكثير من التعليمات البرمجية المعيارية. كان ذلك عندما أصبح مفهوم "نموذج عرض النموذج" (MVVM) سائدًا ، مع إطار عمل Knockout و AngularJS الرائدين في ذلك الوقت ، مما يوفر طبقة تعريفية لجافا سكريبت تتعامل مع هذا التعقيد داخل المكتبة.
MVVM ليس مصطلحًا مستخدَمًا على نطاق واسع اليوم ، وهو نوع من الاختلاف عن المصطلح الأقدم "ربط البيانات".
ربط البيانات
ربط البيانات هو طريقة تعريفية للتعبير عن كيفية مزامنة البيانات بين نموذج وواجهة مستخدم.
توفر جميع أطر عمل واجهة المستخدم الشائعة شكلاً من أشكال ربط البيانات ، وتبدأ برامجها التعليمية بمثال على ربط البيانات.
هنا رابط البيانات في JSX (SolidJS و React):
function HelloWorld() { const name = "Solid or React"; return ( <div>Hello {name}!</div> ) }
ربط البيانات في Lit:
class HelloWorld extends LitElement { @property() name = 'lit'; render() { return html`<p>Hello ${this.name}!</p>`; } }
ربط البيانات في Svelte:
<script> let name = 'world'; </script> <h1>Hello {name}!</h1>
التفاعلية
التفاعلية طريقة تعريفية للتعبير عن انتشار التغيير.
عندما يكون لدينا طريقة للتعبير عن ربط البيانات بشكل إعلاني ، نحتاج إلى طريقة فعالة لإطار العمل لنشر التغييرات.
يقارن محرك React نتيجة التصيير بالنتيجة السابقة ، ويطبق الاختلاف على DOM نفسه. تسمى هذه الطريقة في معالجة انتشار التغيير بـ DOM الظاهري.
في SolidJS ، يتم ذلك بشكل أكثر وضوحًا ، من خلال متجره وعناصره المدمجة. على سبيل المثال ، يتتبع عنصر Show
ما تم تغييره داخليًا ، بدلاً من DOM الظاهري.
في Svelte ، يتم إنشاء الكود "التفاعلي". يعرف Svelte الأحداث التي يمكن أن تسبب تغييرًا ، ويقوم بإنشاء رمز مباشر يرسم الخط الفاصل بين الحدث وتغيير DOM.
في Lit ، يتم تحقيق التفاعلية باستخدام خصائص العنصر ، بالاعتماد بشكل أساسي على التفاعلية المضمنة لعناصر HTML المخصصة.
منطق
عندما يوفر إطار العمل واجهة تعريفية لربط البيانات ، مع تنفيذه للتفاعلية ، فإنه يحتاج أيضًا إلى توفير طريقة ما للتعبير عن بعض المنطق المكتوب تقليديًا بشكل إلزامي. اللبنات الأساسية للمنطق هي "if" و "for" ، وتوفر جميع الأطر الرئيسية بعض التعبير عن هذه اللبنات الأساسية.
الشرطية
بصرف النظر عن ربط البيانات الأساسية مثل الأرقام والسلسلة ، يوفر كل إطار عمل بدائيًا "شرطيًا". في React ، يبدو الأمر كما يلي:
const [hasError, setHasError] = useState(false); return hasError ? <label>Message</label> : null; … setHasError(true);
يوفر SolidJS مكونًا شرطيًا مدمجًا ، Show
:
<Show when={state.error}> <label>Message</label> </Show>
يوفر Svelte التوجيه #if
:
{#if state.error} <label>Message</label> {/if}
في Lit ، يمكنك استخدام عملية ثلاثية صريحة في وظيفة render
:
render() { return this.error ? html`<label>Message</label>`: null; }
القوائم
الإطار البدائي المشترك الآخر هو التعامل مع القوائم. تعد القوائم جزءًا أساسيًا من واجهات المستخدم - قائمة جهات الاتصال والإشعارات وما إلى ذلك - وللعمل بكفاءة ، يجب أن تكون تفاعلية ، وليس تحديث القائمة بأكملها عندما يتغير عنصر بيانات واحد.
في React ، تبدو معالجة القوائم كما يلي:
contacts.map((contact, index) => <li key={index}> {contact.name} </li>)
تستخدم React السمة key
الخاصة للتمييز بين عناصر القائمة ، وتتأكد من عدم استبدال القائمة بأكملها مع كل تصيير.
في SolidJS ، يتم استخدام العناصر المضمنة for
و index
:
<For each={state.contacts}> {contact => <DIV>{contact.name}</DIV> } </For>
داخليًا ، تستخدم SolidJS متجرها الخاص جنبًا إلى جنب مع for
و index
لتحديد العناصر التي سيتم تحديثها عند تغيير العناصر. إنه أكثر وضوحًا من React ، مما يسمح لنا بتجنب تعقيد DOM الافتراضي.
يستخدم Svelte each
توجيه ، والذي يتم نقله بناءً على أدوات التحديث الخاصة به:
{#each contacts as contact} <div>{contact.name}</div> {/each}
يوفر Lit وظيفة repeat
، والتي تعمل بشكل مشابه لتعيين القائمة المستندة إلى key
في React:
repeat(contacts, contact => contact.id, (contact, index) => html`<div>${contact.name}</div>`
نموذج المكون
هناك شيء واحد خارج نطاق هذه المقالة هو نموذج المكون في الأطر المختلفة وكيف يمكن التعامل معه باستخدام عناصر HTML المخصصة.
ملحوظة : هذا موضوع كبير ، وآمل أن أغطيه في مقال قادم لأن هذا الموضوع قد يستغرق وقتًا طويلاً. :)
التكلفة
توفر الأطر ربطًا تصريحيًا للبيانات ، وأساسيات تدفق التحكم (الشروط والقوائم) ، وآلية تفاعلية لنشر التغييرات.
كما أنها توفر أشياء رئيسية أخرى ، مثل طريقة لإعادة استخدام المكونات ، ولكن هذا موضوع لمقال منفصل.
هل الأطر مفيدة؟ نعم. يقدمون لنا كل هذه الميزات المريحة. لكن هل هذا هو السؤال الصحيح الذي يجب طرحه؟ استخدام إطار عمل له تكلفة. دعونا نرى ما هي هذه التكاليف.
حجم الحزمة
عند النظر إلى حجم الحزمة ، أحب النظر إلى الحجم المصغر غير Gzip'd. هذا هو الحجم الأكثر صلة بتكلفة وحدة المعالجة المركزية لتنفيذ JavaScript.
- تبلغ مساحة ReactDOM حوالي 120 كيلو بايت.
- SolidJS حوالي 18 كيلو بايت.
- مضاءة حوالي 16 كيلو بايت.
- يبلغ حجم Svelte حوالي 2 كيلوبايت ، لكن حجم الكود الذي تم إنشاؤه يختلف.
يبدو أن أطر العمل الحالية تقوم بعمل أفضل من React في الحفاظ على حجم الحزمة صغيرًا. يتطلب DOM الظاهري الكثير من JavaScript.
يبني
بطريقة ما اعتدنا على "بناء" تطبيقات الويب الخاصة بنا. من المستحيل بدء مشروع أمامي بدون إعداد Node.js وحزمة مثل Webpack ، والتعامل مع بعض تغييرات التكوين الحديثة في حزمة المبتدئين Babel-TypeScript ، وكل موسيقى الجاز هذه.
كلما كان حجم حزمة الإطار أكثر تعبيرًا وصغر حجمه ، زاد عبء أدوات البناء ووقت النقل.
تدعي Svelte أن DOM الظاهري هو عبء خالص. أوافق ، ولكن ربما تكون محركات "البناء" (كما هو الحال مع Svelte و SolidJS) ومحركات القوالب المخصصة من جانب العميل (كما هو الحال مع Lit) هي أيضًا من نوع مختلف تمامًا؟
تصحيح
مع البناء والشفاء يأتي نوع مختلف من التكلفة.
يختلف الكود الذي نراه عند استخدام تطبيق الويب أو تصحيح أخطائه تمامًا عما كتبناه. نعتمد الآن على أدوات تصحيح أخطاء خاصة ذات جودة متفاوتة لإجراء هندسة عكسية لما يحدث على موقع الويب وربطه بالأخطاء الموجودة في التعليمات البرمجية الخاصة بنا.
في React ، لا يكون مكدس الاستدعاءات "ملكًا لك" أبدًا - يتعامل React مع الجدولة نيابةً عنك. هذا يعمل بشكل رائع عندما لا يكون هناك أخطاء. لكن حاول تحديد سبب إعادة عرض الحلقة اللانهائية وستكون في عالم من الألم.
في Svelte ، حجم حزمة المكتبة نفسها صغير ، لكنك ستقوم بشحن مجموعة كاملة من التعليمات البرمجية المشفرة التي تم إنشاؤها وتصحيحها والتي تمثل تنفيذ Svelte للتفاعل ، والمخصصة لاحتياجات التطبيق الخاص بك.
مع Lit ، لا يتعلق الأمر بالبناء ، ولكن لتصحيحه بشكل فعال ، يجب أن تفهم محرك النموذج الخاص به. قد يكون هذا هو السبب الأكبر وراء تشكك مشاعري تجاه الأطر.
عندما تبحث عن حلول تعريفية مخصصة ، ينتهي بك الأمر بمزيد من تصحيح الأخطاء الإلزامي المؤلم. تستخدم الأمثلة في هذا المستند Typescript لمواصفات API ، لكن الكود نفسه لا يتطلب الترجمة.
ترقيات
في هذا المستند ، ألقيت نظرة على أربعة أطر عمل ، ولكن هناك إطارات أكثر مما يمكنني الاعتماد عليه (AngularJS و Ember.js و Vue.js ، على سبيل المثال لا الحصر). هل يمكنك الاعتماد على إطار العمل ومطوريه ومشاركته الذهنية ونظامه البيئي للعمل من أجلك أثناء تطوره؟
الشيء الوحيد الذي يكون أكثر إحباطًا من إصلاح الأخطاء الخاصة بك هو الحاجة إلى إيجاد حلول بديلة لأخطاء النظام. والشيء الأكثر إحباطًا من أخطاء الإطار هو الأخطاء التي تحدث عندما تقوم بترقية إطار عمل إلى إصدار جديد دون تعديل التعليمات البرمجية الخاصة بك.
صحيح أن هذه المشكلة موجودة أيضًا في المتصفحات ، ولكن عندما تحدث ، فإنها تحدث للجميع ، وفي معظم الحالات يكون الإصلاح أو الحل المنشور وشيكًا. أيضًا ، تستند معظم الأنماط الواردة في هذا المستند إلى واجهات برمجة تطبيقات لمنصة الويب الناضجة ؛ ليس هناك دائما حاجة للذهاب مع حافة النزيف.
ملخص
لقد تعمقنا قليلاً في فهم أطر عمل المشكلات الأساسية التي تحاول حلها وكيفية حلها ، مع التركيز على ربط البيانات والتفاعل والشروط والقوائم. نظرنا أيضًا إلى التكلفة.
في الجزء الثاني ، سنرى كيف يمكن معالجة هذه المشكلات دون استخدام إطار عمل على الإطلاق ، وما يمكننا التعلم منه. ابقوا متابعين!
شكر خاص للأفراد التالية أسماؤهم على المراجعات الفنية: يهوناتان دانيف ، وتوم بيغيلاجزين ، وبنجامين جرينباوم ، ونيك ريبال ، ولويس لازاريس.