الترحيل من jQuery إلى Vue.js

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



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

نظرة عامة على المشروع


سنقوم بتطوير حساب إلكتروني بسيط بناءً على قالب مفتوح المصدر من Sparksuite.

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

هذا هو قالب الحساب الذي نريد العمل معه.


فاتورة إلكترونية

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

قمت بتعديل القالب عن طريق تحويل كود HTML للسلسلة الفارغة إلى النموذج التالي:

<tr class="item">  <td><input value="" /></td>  <td>$<input type="number" value="0" /></td>  <td><input type="number" value="1" /></td>  <td>$0.00</td> </tr> 

تطوير مشروع JQuery


أولاً ، دعنا نلقي نظرة على كيفية حل مشكلتنا باستخدام jQuery.

 $('table').on('mouseup keyup', 'input[type=number]', calculateTotals); 

نربط مستمع الحدث بالجدول. سوف يستدعي هذا المستمع دالة calculateTotals عندما تتغير القيم المقابلة لتكلفة الوحدة للمنتج أو تغير كميته:

 function calculateTotals()  { const subtotals = $('.item').map((idx, val)  => calculateSubtotal(val)).get(); const total = subtotals.reduce((a, v)  => a + Number(v), 0); $('.total td:eq(1)').text(formatAsCurrency(total)); } 

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

 function calculateSubtotal(row) { const $row = $(row); const inputs = $row.find('input'); const subtotal = inputs[1].value * inputs[2].value; $row.find('td:last').text(formatAsCurrency(subtotal)); return subtotal; } 

في هذا الكود ، نأخذ مراجع لجميع حقول <input> الموجودة في السطر ، ثم نضرب القيم المخزنة في الحقلين الثاني والثالث للحصول على قيمة subtotal . ثم يتم إدراج هذه القيمة في الخلية الأخيرة من الصف. نقوم بتنسيق هذه القيمة باستخدام وظيفة formatAsCurrency . إليك رمزها:

 function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; } 

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

 $('.btn-add-row').on('click', () => { const $lastRow = $('.item:last'); const $newRow = $lastRow.clone(); $newRow.find('input').val(''); $newRow.find('td:last').text('$0.00'); $newRow.insertAfter($lastRow); $newRow.find('input:first').focus(); }); 

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

في ما يلي مثال عملي لحساب يتم تنفيذ ميزاته التفاعلية باستخدام أدوات jQuery.

مساوئ الحل القائم على jQuery


دعونا نسأل أنفسنا ما هي عيوب النهج أعلاه ، أو بالأحرى ، كيفية تحسين مشروعنا.

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

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

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

ترجمة المشروع على Vue


لنتحدث الآن عن كيفية إعادة إنشاء نفس الوظيفة على Vue. للاستفادة من إمكانات Vue ، ما عليك سوى توصيل هذه المكتبة بصفحة ويب عادية ، تمامًا كما تفعل مع jQuery. أي أنه لا توجد حاجة لاستخدام النظام لوحدات التعبئة أو جهاز التحويل ؛ لا تحتاج إلى تقسيم التطبيق إلى مكونات وتفكيك الكود الخاص بها إلى ملفات .vue.

نبدأ ترجمة المشروع إلى Vue عن طريق استبدال محتويات <script> :

 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 

الخطوة التالية هي إنشاء نسخة جديدة من Vue:

 const app = new Vue({ el: 'table' }); 

المعلمة الوحيدة لتمريرها إلى مُنشئ مثيل Vue الجديد هي el . هذا محدد (نفس المستخدم في jQuery) يحدد جزء المستند الذي نريد التحكم فيه باستخدام Vue.

يمكن تعيين مهام Vue بأحجام مختلفة - بدءًا من إدارة صفحة كاملة (في حالة تطبيق من صفحة واحدة على سبيل المثال) ، إلى جزء صغير محاط <div> . في مثالنا ، ستكون Vue مسؤولة عن العمل مع جدول HTML.

البيانات


أضف بيانات شرطية لثلاثة أسطر من المستند إلى مثيل Vue:

 const app = new Vue({ el: 'table', data: {   items: [     { description: 'Website design', quantity: 1, price: 300 },     { description: 'Hosting (3 months)', quantity: 1, price: 75 },     { description: 'Domain name (1 year)', quantity: 1, price: 10 },   ] } }); 

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

يساعد Vue في الحفاظ على حالة التطبيق منفصلة عن العرض التقديمي (أي من DOM) ، ويساعد على تخزين الحالة مركزيًا في مكان واحد ، وهو مصدر واحد للبيانات الموثوقة.

تعديل القالب


الآن قم بتكوين القالب بحيث يعرض البيانات المخزنة في كائن data . نظرًا لأننا أخبرنا إطار العمل بأننا نريد العمل مع جدول باستخدام مثيل Vue ، يمكننا استخدام بنية قالب Vue في كود HTML الذي يصف هذا الجدول لإخبار Vue بكيفية تقديم الجدول وكيفية استخدامه للعمل.

باستخدام السمة v-for ، يمكننا استنتاج كتلة من كود HTML لكل عنصر من مصفوفة items :

 <tr class="item" v-for="item in items"> </tr> 

سيكرر Vue هذا الترميز لكل عنصر من عناصر المصفوفة (أو الكائن) الذي يتم تمريره إلى v-for . هذا يسمح لنا بالإشارة إلى كل عنصر في الحلقة. في هذه الحالة ، يتم تمثيل هذا العنصر بواسطة متغير item . نظرًا لأن Vue يشرف على خصائص كائن data ، فسيقوم إطار العمل بتحديث الترميز ديناميكيًا عند تغيير محتويات صفيف items . كل ما نحتاجه هو تعديل الحالة بإضافة عناصر أو إزالتها ، وسيقوم Vue بتحديث واجهة المستخدم تلقائيًا.

بالإضافة إلى ذلك ، نحتاج إلى إضافة حقول إدخال يمكن للمستخدم ملؤها عن طريق إدخال وصف للمنتج وسعر الوحدة والكمية:

 <td><input v-model="item.description" /></td> <td>$<input type="number" v-model="item.price" /></td> <td><input type="number" v-model="item.quantity" /></td> <td>${{ item.price * item.quantity }}</td> 

هنا نستخدم السمة v-model لتهيئة ربط البيانات ثنائي الاتجاه بين حقول الإدخال والخصائص في نموذج البيانات. وهذا يعني أن تغيير البيانات في حقول الإدخال سيؤدي إلى تغييرها في النموذج ، والعكس صحيح.

في الخلية الأخيرة ، نستخدم الأقواس المتعرجة {{}} ، ونستخدمها لعرض النص. يمكنك استخدام أي تعبير JavaScript عامل بين قوسين. في هذه الحالة ، نقوم بضرب خاصيتي العنصر وإخراج ما حدث. مرة أخرى ، يشرف Vue على نموذج البيانات ؛ سيؤدي تغيير أي موقع إلى إعادة التعبير تلقائيًا.

الأحداث والأساليب


الآن لدينا قالب جاهز لعرض مجموعة items ونواجه سؤال حول كيفية إضافة صفوف جديدة إلى الجدول. نظرًا لأن Vue سيخرج كل شيء موجود في صفيف items إلى الصفحة ، لعرض سلسلة فارغة على الصفحة ، يكفي تمرير كائن Vue مع أي قيم يجب أن تكون موجودة في صفيف items .

لإنشاء دالة يمكن الوصول إليها من القالب ، تحتاج إلى تمرير هذه الوظيفة إلى مثيل Vue كخاصية لكائن methods :

 const app = new Vue({ // ... methods: {   myMethod() {} }, // ... }) 

addRow طريقة addRow ، والتي يمكن استدعاؤها لإضافة عنصر جديد إلى مصفوفة items :

 methods: { addRow() {   this.items.push({ description: '', quantity: 1, price: 0 }); }, }, 

يرجى ملاحظة أن أي طرق نقوم بإنشائها ستكون مرتبطة تلقائيًا بمثيل Vue ، مما سيمكننا من الوصول إلى خصائص كائن data والطرق الأخرى كخصائص this من هذه الطرق.

حتى الآن لدينا طريقة. كيف تسميها بالضغط على زر Add row ؟ لإضافة مستمعي الأحداث إلى عناصر التحكم في قالب ، يستخدم Vue بناء v-on:event-name .

 <button class="btn-add-row" @click="addRow">Add row</button> 

يوفر Vue أيضًا اختصارًا لـ v-on : build ، والذي يبدو مثل @ . يتم استخدامه في مقتطف الرمز أعلاه. يمكنك استخدام أي طريقة من مثيل Vue كمعالج للأحداث.

خصائص محسوبة


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

لهذا الغرض ، يمكننا استخدام الطريقة المعتادة ، ولكن أعتقد أنه في هذه الحالة ، فإن ما يسمى بالملكية المحسوبة هو الأفضل بالنسبة لنا. يشبه العمل مع هذه الخصائص العمل أعلاه مع الأساليب. أي لإنشاء مثل هذه الخصائص ، يتم تمرير كائن computed إلى computed Vue ، يحتوي على الدوال التي نريد استخدام نتائج تنفيذها في القالب:

 const app = new Vue({ // ... computed: {   total() {     return this.items.reduce((acc, item) => acc + (item.price * item.quantity), 0);   } } }); 

الآن يمكن الرجوع إلى الخاصية المحسوبة من القالب:

 <tr class="total"> <td colspan="3"></td> <td>Total: ${{ total }}</td> 

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

إذا استخدمنا الطريقة لحساب المبلغ الإجمالي للمستند ، فسيتم إجراء الحسابات في كل مرة يتم فيها إخراج القالب. ولكن نظرًا لأننا نستخدم الخاصية المحسوبة ، يتم إعادة حساب المبلغ الإجمالي فقط عند تغيير قيم quantity أو price في صفوف الجدول.

مرشحات


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

بدلاً من تعديل الكود المسؤول عن حساب القيمة الإجمالية للخط ، والكود الذي يحسب إجمالي المبلغ للمستند ، يمكننا الاستفادة من إمكانيات تنسيق البيانات الملائمة لـ Vue. حول الفلاتر.

كما فهمت على الأرجح بالفعل ، من أجل إنشاء عامل تصفية ، يكفي تمرير كائن ( filters في هذه الحالة) باستخدام المفتاح المقابل لمثيل Vue:

 const app = new Vue({ // ... filters: {   currency(value) {     return value.toFixed(2);   } } }); 

هنا قمنا بإنشاء مرشح بسيط للغاية يسمى currency . تستدعي الدالة toFixed(2) للقيمة العددية التي يتم تمريرها إليها وترجع النتيجة. يمكنك استخدام هذا الفلتر في النموذج على النحو التالي:

 <td>Total: ${{ total | currency }}</td> 

هنا هو التنفيذ النهائي لمشروعنا على Vue.

الملخص


إذا قارنت نسختين من المشروع ، أحدهما تم إنشاؤه باستخدام jQuery ، والثاني مكتوب بلغة Vue ، فستلاحظ نقاط القوة التالية للتطبيق القائم على Vue:

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

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

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

إذا كنت مهتمًا بموضوع تطوير تطبيقات الويب على Vue ، فألق نظرة على هذه المادة . بالإضافة إلى ذلك ، إذا كنت تفكر في نقل المشاريع من JS خالص إلى Vue ، فإليك منشورنا الأخير حول هذا الموضوع.

أعزائي القراء! هل تستخدم jQuery؟ وإذا كنت تستخدمه ، فهل تخطط لتغيير هذه المكتبة إلى شيء آخر ، على سبيل المثال - إلى Vue؟

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


All Articles