paint-brush
تعزيز العميل باستخدام Vue.jsبواسطة@nfrankel
تاريخ جديد

تعزيز العميل باستخدام Vue.js

بواسطة Nicolas Fränkel17m2024/09/26
Read on Terminal Reader

طويل جدا؛ ليقرأ

في هذا المنشور، اتخذت أولى خطواتي في تعزيز تطبيق SSR باستخدام Vue. كان الأمر بسيطًا للغاية. كانت أكبر مشكلة واجهتها هي استبدال Vue لقالب السطر: لم أقرأ الوثائق على نطاق واسع وتجاهلت سمة is.
featured image - تعزيز العميل باستخدام Vue.js
Nicolas Fränkel HackerNoon profile picture
0-item


في منشوري السابق ، وضعت الأساس للبناء عليه؛ والآن هو الوقت المناسب للبدء "بشكل حقيقي".


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

وضع العمل

لقد شرحت WebJars وThymeleaf في المنشور الأخير. إليك الإعداد على جانب الخادم وعلى جانب العميل.

من جانب الخادم

فيما يلي كيفية دمج كليهما في POM:


 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--1--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <!--2--> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <!--3--> <version>0.52</version> </dependency> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>vue</artifactId> <!--4--> <version>3.4.34</version> </dependency> </dependencies>
  1. Spring Boot نفسه؛ قررت اتباع النهج العادي غير التفاعلي
  2. تكامل Spring Boot Thymeleaf
  3. محدد موقع WebJars، لتجنب تحديد إصدار Vue على جانب العميل
  4. انظر، أخيرا!


أستخدم Kotlin Router وBean DSLs على جانب Spring Boot:


 fun vue(todos: List<Todo>) = router { //1 GET("/vue") { ok().render("vue", mapOf("title" to "Vue.js", "todos" to todos)) //2-3 } }
  1. تمرير قائمة ثابتة من كائنات Todo
  2. انظر أدناه
  3. مرر النموذج إلى Thymeleaf


إذا كنت معتادًا على تطوير واجهات برمجة التطبيقات، فأنت على دراية بوظيفة body() ؛ فهي تعيد الحمولة مباشرةً، ربما بتنسيق JSON. تمرر وظيفة render() التدفق إلى تقنية العرض، في هذه الحالة، Thymeleaf. وهي تقبل معاملين:


  1. اسم العرض. بشكل افتراضي، المسار هو /templates والبادئة هي .html ؛ في هذه الحالة، يتوقع Thymeleaf عرضًا في /templates/vue.html
  2. خريطة نموذجية لأزواج القيمة الرئيسية

جانب العميل

هذا هو الكود الموجود على جانب HTML:


 <script th:src="@{/webjars/axios/dist/axios.js}" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script> <!--1--> <script th:src="@{/webjars/vue/dist/vue.global.js}" src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script> <!--2--> <script th:src="@{/vue.js}" src="../static/vue.js"></script> <!--3--> <script th:inline="javascript"> /*<![CDATA[*/ window.vueData = { <!--4--> title: /*[[${ title }]]*/ 'A Title', todos: /*[[${ todos }]]*/ [{ 'id': 1, 'label': 'Take out the trash', 'completed': false }] }; /*]]>*/ </script>
  1. يساعد Axios في إجراء طلبات HTTP
  2. عرض نفسه
  3. كود العميل الخاص بنا
  4. ضبط البيانات


كما تم شرحه في مقال الأسبوع الماضي، فإن إحدى فوائد Thymeleaf هي أنه يسمح بكل من عرض الملفات الثابتة والعرض من جانب الخادم. ولجعل السحر يعمل، أقوم بتحديد مسار من جانب العميل، أي src ، ومسار من جانب الخادم، أي th:src .


كود Vue

الآن، دعونا ننتقل إلى كود Vue.

نريد تنفيذ العديد من الميزات:


  1. بعد تحميل الصفحة، يجب أن تعرض الصفحة جميع عناصر Todo
  2. عند النقر فوق مربع الاختيار "مكتمل Todo ، يجب تعيين/إلغاء تعيين سمة completed "
  3. عند النقر على زر التنظيف ، يتم حذف جميع Todo المكتملة
  4. عند النقر على زر الإضافة ، يجب إضافة Todo إلى قائمة Todo بالقيم التالية:
    • id : معرف محسوب من جانب الخادم باعتباره الحد الأقصى لجميع المعرفات الأخرى بالإضافة إلى 1
    • label : قيمة حقل التسمية label
    • completed : تم ضبطه على false

خطواتنا الأولى في Vue

الخطوة الأولى هي تمهيد الإطار. لقد قمنا بالفعل بإعداد المرجع لملف vue.js المخصص أعلاه.


 document.addEventListener('DOMContentLoaded', () => { //1 // The next JavaScript code snippets will be inside the block }
  1. قم بتشغيل الكتلة عندما ينتهي تحميل DOM


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


 <div id="app"> </div>


على جانب JavaScript، نقوم بإنشاء تطبيق ، عن طريق تمرير محدد CSS الخاص بـ HTML السابق <div> .


 Vue.createApp({}).mount('#app');


في هذه المرحلة، نقوم بتشغيل Vue عند تحميل الصفحة، ولكن لا يحدث أي شيء مرئي.


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


لنبدأ بقالب جذر يمكنه عرض العنوان.


 <template id="todos-app"> <!--1--> <h1>{{ title }}</h1> <!--2--> </template>
  1. تعيين معرف لسهولة الربط
  2. استخدم خاصية title ؛ لا يزال يتعين إعدادها


من ناحية JavaScript، يجب علينا إنشاء الكود الإداري.


 const TodosApp = { props: ['title'], //1 template: document.getElementById('todos-app').innerHTML, }
  1. أعلن عن خاصية title ، تلك المستخدمة في قالب HTML


وأخيرًا، يجب علينا تمرير هذا الكائن عند إنشاء التطبيق:


 Vue.createApp({ components: { TodosApp }, //1 render() { //2 return Vue.h(TodosApp, { //3 title: window.vueData.title, //4 }) } }).mount('#app');
  1. تكوين المكون
  2. يتوقع Vue وظيفة render()
  3. يقوم h() لـ hyperscript بإنشاء عقدة افتراضية من الكائن وخصائصه
  4. قم بتهيئة خاصية title بالقيمة التي تم إنشاؤها على جانب الخادم


في هذه المرحلة، يعرض Vue العنوان.

التفاعلات الأساسية

في هذه المرحلة، يمكننا تنفيذ الإجراء عندما ينقر المستخدم على مربع الاختيار: يجب تحديثه في حالة جانب الخادم.

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


هذا هو كود قالب خط البداية، JavaScript و HTML على التوالي:


 const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML }
 <template id="todo-line"> <tr> <td>{{ todo.id }}</td> <!--1--> <td>{{ todo.label }}</td> <!--2--> <td> <label> <input type="checkbox" :checked="todo.completed" /> </label> </td> </tr> </template>
  1. عرض معرف Todo
  2. عرض علامة Todo
  3. حدد المربع إذا كانت السمة completed true


يسمح Vue بالتعامل مع الأحداث عبر صيغة @ .


 <input type="checkbox" :checked="todo.completed" @click="check" />


يستدعي Vue دالة check() الخاصة بالقالب عندما ينقر المستخدم على السطر. نقوم بتعريف هذه الدالة في معلمة setup() :


 const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML, setup(props) { //1 const check = function (event) { //2 const { todo } = props axios.patch( //3 `/api/todo/${todo.id}`, //4 { checked: event.target.checked } //5 ) } return { check } //6 } }
  1. اقبل مجموعة props ، حتى نتمكن من الوصول إليها لاحقًا
  2. يقوم Vue بتمرير event الذي أدى إلى المكالمة
  3. Axios عبارة عن مكتبة JavaScript تعمل على تبسيط مكالمات HTTP
  4. يجب أن يوفر جانب الخادم واجهة برمجة التطبيقات (API)؛ وهو خارج نطاق هذه التدوينة، ولكن لا تتردد في التحقق من كود المصدر.
  5. حمولة JSON
  6. نقوم بإرجاع جميع الوظائف المحددة لجعلها قابلة للوصول من HTML

نموذج جانب العميل

في القسم السابق، ارتكبت خطأين:


  • لم أتمكن من إدارة أي نموذج محلي
  • لم أستخدم طريقة استدعاء استجابة HTTP


سنقوم بذلك من خلال تنفيذ الميزة التالية، وهي تنظيف المهام المكتملة.


نحن نعلم الآن كيفية التعامل مع الأحداث عبر Vue:


 <button class="btn btn-warning" @click="cleanup">Cleanup</button>


في كائن TodosApp ، نضيف وظيفة بنفس الاسم:


 const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const cleanup = function() { //1 axios.delete('/api/todo:cleanup').then(response => { //1 state.value.todos = response.data //2-3 }) } return { cleanup } //1 } }
  1. كما هو مذكور اعلاه
  2. يوفر Axios تحويل JSON تلقائيًا لمكالمة HTTP
  3. state هي المكان الذي نخزن فيه النموذج


في دلالات Vue، يعتبر نموذج Vue غلافًا للبيانات التي نريد أن تكون تفاعلية . تعني التفاعلية الربط ثنائي الاتجاه بين العرض والنموذج. يمكننا جعل قيمة موجودة تفاعلية عن طريق تمريرها إلى طريقة ref() :


في Composition API، الطريقة الموصى بها لإعلان الحالة التفاعلية هي استخدام الدالة ref() .


ref() تأخذ الوسيطة وتعيدها مغلفة داخل كائن ref مع خاصية .value.


للوصول إلى المراجع في قالب المكون، قم بإعلانها وإرجاعها من دالة setup() الخاصة بالمكون.


-- إعلان الحالة التفاعلية


دعونا نفعل ذلك:


 const state = ref({ title: window.vueData.title, //1-2 todos: window.vueData.todos, //1 }) createApp({ components: { TodosApp }, setup() { return { ...state.value } //3-4 }, render() { return h(TodosApp, { todos: state.value.todos, //5 title: state.value.title, //5 }) } }).mount('#app');
  1. احصل على مجموعة البيانات في صفحة HTML، عبر Thymeleaf، كما هو موضح أعلاه
  2. نقوم بتغيير الطريقة التي نضبط بها title . ليس هذا ضروريًا نظرًا لعدم وجود ارتباط ثنائي الاتجاه - فنحن لا نقوم بتحديث العنوان على جانب العميل، لكنني أفضل الحفاظ على تماسك التعامل عبر جميع القيم
  3. إرجاع المراجعات، وفقًا لتوقعات Vue
  4. انظر يا أمي، أنا أستخدم عامل الانتشار في JavaScript
  5. تكوين الكائن المنسوب من state


في هذه المرحلة، أصبح لدينا نموذج تفاعلي من جانب العميل.


على جانب HTML، نستخدم سمات Vue ذات الصلة:


 <tbody> <tr is="vue:todo-line" v-for="todo in todos" :key="todo.id" :todo="todo"></tr> <!--1-2--> </tbody>
  1. تكرار قائمة كائنات Todo
  2. is is ضرورية للتعامل مع الطريقة التي يحلل بها المتصفح HTML. راجع وثائق Vue لمزيد من التفاصيل

لقد وصفت القالب المقابل أعلاه.

تحديث النموذج

يمكننا الآن تنفيذ ميزة جديدة: إضافة Todo جديدة من العميل. عند النقر فوق الزر "إضافة" ، نقرأ قيمة حقل التسمية ، ونرسل البيانات إلى واجهة برمجة التطبيقات، ونقوم بتحديث النموذج بالاستجابة.


إليك الكود المحدث:


 const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const label = ref('') //1 const create = function() { //2 axios.post('/api/todo', { label: label.value }).then(response => { state.value.todos.push(response.data) //3 }).then(() => { label.value = '' //4 }) } const cleanup = function() { axios.delete('/api/todo:cleanup').then(response => { state.value.todos = response.data //5 }) } return { label, create, cleanup } } }
  1. إنشاء غلاف تفاعلي حول العنوان يقتصر نطاقه على الوظيفة
  2. دالة create() المناسبة
  3. أضف كائن JSON الجديد الذي تم إرجاعه بواسطة استدعاء API إلى قائمة Todo
  4. إعادة تعيين قيمة الحقل
  5. استبدال القائمة بأكملها عند الحذف؛ الآلية هي نفسها


من ناحية HTML، نضيف زرًا ونربطه بوظيفة create() . وبالمثل، نضيف حقل Label ونربطه بالنموذج.


 <form> <div class="form-group row"> <label for="new-todo-label" class="col-auto col-form-label">New task</label> <div class="col-10"> <input type="text" id="new-todo-label" placeholder="Label" class="form-control" v-model="label" /> </div> <div class="col-auto"> <button type="button" class="btn btn-success" @click="create">Add</button> </div> </div> </form>


يقوم Vue بربط دالة create() بزر HTML. ويقوم باستدعائها بشكل غير متزامن ويقوم بتحديث قائمة Todo التفاعلية بالعنصر الجديد الذي تم إرجاعه بواسطة الاستدعاء. ونقوم بنفس الشيء بالنسبة لزر Cleanup ، لإزالة كائنات Todo المحددة.


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

خاتمة

في هذا المنشور، اتخذت أولى خطواتي في تعزيز تطبيق SSR باستخدام Vue. كان الأمر بسيطًا للغاية. كانت أكبر مشكلة واجهتها هي استبدال Vue لقالب السطر: لم أقرأ الوثائق على نطاق واسع وتجاهلت السمة is .


ومع ذلك، كان عليّ كتابة عدد لا بأس به من أسطر JavaScript، على الرغم من أنني استخدمت Axios لمساعدتي في استدعاءات HTTP ولم أتمكن من إدارة الأخطاء.


في المنشور التالي، سأقوم بتنفيذ نفس الميزات مع Alpine.js.


يمكن العثور على الكود المصدر الكامل لهذه التدوينة على GitHub:

اذهب أبعد من ذلك: