في ، وضعت الأساس للبناء عليه؛ والآن هو الوقت المناسب للبدء "بشكل حقيقي". منشوري السابق سمعت الكثير عن بالإضافة إلى ذلك، أخبرني أحد الأصدقاء الذي تحول من مطور إلى مدير بأشياء جيدة عن Vue، مما أثار اهتمامي بشكل أكبر. قررت إلقاء نظرة عليه: سيكون أول إطار عمل JavaScript "خفيف الوزن" أدرسه - من وجهة نظر مبتدئ، وهو ما أنا عليه. Vue.js. وضع العمل لقد شرحت 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> Spring Boot نفسه؛ قررت اتباع النهج العادي غير التفاعلي تكامل Spring Boot Thymeleaf محدد موقع WebJars، لتجنب تحديد إصدار Vue على جانب العميل انظر، أخيرا! أستخدم 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 } } تمرير قائمة ثابتة من كائنات Todo انظر أدناه مرر النموذج إلى Thymeleaf إذا كنت معتادًا على تطوير واجهات برمجة التطبيقات، فأنت على دراية بوظيفة ؛ فهي تعيد الحمولة مباشرةً، ربما بتنسيق JSON. تمرر وظيفة التدفق إلى تقنية العرض، في هذه الحالة، Thymeleaf. وهي تقبل معاملين: body() render() اسم العرض. بشكل افتراضي، المسار هو والبادئة هي ؛ في هذه الحالة، يتوقع Thymeleaf عرضًا في /templates .html /templates/vue.html خريطة نموذجية لأزواج القيمة الرئيسية جانب العميل هذا هو الكود الموجود على جانب HTML: <script th:src="@{/webjars/axios/dist/axios.js}" src="https://cdn.jsdelivr.net/npm/axios@1.7/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> يساعد في إجراء طلبات HTTP Axios عرض نفسه كود العميل الخاص بنا ضبط البيانات كما تم شرحه في مقال الأسبوع الماضي، فإن إحدى فوائد Thymeleaf هي أنه يسمح بكل من عرض الملفات الثابتة والعرض من جانب الخادم. ولجعل السحر يعمل، أقوم بتحديد مسار من جانب العميل، ، ومسار من جانب الخادم، . أي src أي th:src كود Vue الآن، دعونا ننتقل إلى كود Vue. نريد تنفيذ العديد من الميزات: بعد تحميل الصفحة، يجب أن تعرض الصفحة جميع عناصر Todo عند النقر فوق مربع الاختيار "مكتمل ، يجب تعيين/إلغاء تعيين سمة " Todo completed عند النقر على زر ، يتم حذف جميع المكتملة التنظيف Todo عند النقر على زر ، يجب إضافة إلى قائمة بالقيم التالية: الإضافة Todo Todo : معرف محسوب من جانب الخادم باعتباره الحد الأقصى لجميع المعرفات الأخرى بالإضافة إلى 1 id : قيمة حقل label التسمية label : تم ضبطه على completed false خطواتنا الأولى في Vue الخطوة الأولى هي تمهيد الإطار. لقد قمنا بالفعل بإعداد المرجع لملف المخصص أعلاه. vue.js document.addEventListener('DOMContentLoaded', () => { //1 // The next JavaScript code snippets will be inside the block } قم بتشغيل الكتلة عندما ينتهي تحميل DOM الخطوة التالية هي السماح لـ Vue بإدارة جزء من الصفحة. على جانب HTML، يجب أن نقرر الجزء الأعلى مستوى الذي يديره Vue. يمكننا اختيار عشوائيًا وتغييره لاحقًا إذا لزم الأمر. <div> <div id="app"> </div> على جانب JavaScript، نقوم بإنشاء ، عن طريق تمرير محدد CSS الخاص بـ HTML السابق . تطبيق <div> Vue.createApp({}).mount('#app'); في هذه المرحلة، نقوم بتشغيل Vue عند تحميل الصفحة، ولكن لا يحدث أي شيء مرئي. الخطوة التالية هي إنشاء Vue. قالب Vue هو HTML عادي يتم إدارته بواسطة Vue. يمكنك تعريف Vue في Javascript، لكنني أفضل القيام بذلك على صفحة HTML. قالب <template> لنبدأ بقالب جذر يمكنه عرض العنوان. <template id="todos-app"> <!--1--> <h1>{{ title }}</h1> <!--2--> </template> تعيين معرف لسهولة الربط استخدم خاصية ؛ لا يزال يتعين إعدادها title من ناحية JavaScript، يجب علينا إنشاء الكود الإداري. const TodosApp = { props: ['title'], //1 template: document.getElementById('todos-app').innerHTML, } أعلن عن خاصية ، تلك المستخدمة في قالب HTML title وأخيرًا، يجب علينا تمرير هذا الكائن عند إنشاء التطبيق: Vue.createApp({ components: { TodosApp }, //1 render() { //2 return Vue.h(TodosApp, { //3 title: window.vueData.title, //4 }) } }).mount('#app'); تكوين المكون يتوقع Vue وظيفة render() يقوم لـ بإنشاء عقدة افتراضية من الكائن وخصائصه h() hyperscript قم بتهيئة خاصية بالقيمة التي تم إنشاؤها على جانب الخادم 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> عرض معرف Todo عرض علامة Todo حدد المربع إذا كانت السمة 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 } } اقبل مجموعة ، حتى نتمكن من الوصول إليها لاحقًا props يقوم Vue بتمرير الذي أدى إلى المكالمة event Axios عبارة عن مكتبة JavaScript تعمل على تبسيط مكالمات HTTP يوفر جانب الخادم واجهة برمجة التطبيقات (API)؛ وهو خارج نطاق هذه التدوينة، ولكن لا تتردد في التحقق من كود المصدر. يجب أن حمولة JSON نقوم بإرجاع جميع الوظائف المحددة لجعلها قابلة للوصول من 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 } } كما هو مذكور اعلاه يوفر Axios تحويل JSON تلقائيًا لمكالمة HTTP هي المكان الذي نخزن فيه state النموذج في دلالات Vue، يعتبر نموذج Vue غلافًا للبيانات التي نريد أن تكون . تعني التفاعلية الربط ثنائي الاتجاه بين العرض والنموذج. يمكننا جعل قيمة موجودة تفاعلية عن طريق تمريرها إلى طريقة : تفاعلية ref() في Composition API، الطريقة الموصى بها لإعلان الحالة التفاعلية هي استخدام الدالة . ref() تأخذ الوسيطة وتعيدها مغلفة داخل كائن ref مع خاصية .value. ref() للوصول إلى المراجع في قالب المكون، قم بإعلانها وإرجاعها من دالة الخاصة بالمكون. 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'); احصل على مجموعة البيانات في صفحة HTML، عبر Thymeleaf، كما هو موضح أعلاه نقوم بتغيير الطريقة التي نضبط بها . ليس هذا ضروريًا نظرًا لعدم وجود ارتباط ثنائي الاتجاه - فنحن لا نقوم بتحديث العنوان على جانب العميل، لكنني أفضل الحفاظ على تماسك التعامل عبر جميع القيم title إرجاع المراجعات، وفقًا لتوقعات Vue انظر يا أمي، أنا أستخدم عامل الانتشار في JavaScript تكوين الكائن المنسوب من state في هذه المرحلة، أصبح لدينا نموذج من جانب العميل. تفاعلي على جانب HTML، نستخدم سمات Vue ذات الصلة: <tbody> <tr is="vue:todo-line" v-for="todo in todos" :key="todo.id" :todo="todo"></tr> <!--1-2--> </tbody> تكرار قائمة كائنات Todo is ضرورية للتعامل مع الطريقة التي يحلل بها المتصفح HTML. راجع لمزيد من التفاصيل is وثائق 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 } } } إنشاء غلاف تفاعلي حول العنوان يقتصر نطاقه على الوظيفة دالة المناسبة create() أضف كائن JSON الجديد الذي تم إرجاعه بواسطة استدعاء API إلى قائمة Todo إعادة تعيين قيمة الحقل استبدال القائمة بأكملها عند الحذف؛ الآلية هي نفسها من ناحية 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 بربط دالة بزر HTML. ويقوم باستدعائها بشكل غير متزامن ويقوم بتحديث قائمة التفاعلية بالعنصر الجديد الذي تم إرجاعه بواسطة الاستدعاء. ونقوم بنفس الشيء بالنسبة لزر ، لإزالة كائنات المحددة. create() Todo Cleanup Todo لاحظ أنني لم أقم عمدًا بتنفيذ أي كود لمعالجة الأخطاء لتجنب جعل الكود أكثر تعقيدًا مما هو ضروري. سأتوقف هنا حيث اكتسبنا ما يكفي من الأفكار للتجربة الأولى. خاتمة في هذا المنشور، اتخذت أولى خطواتي في تعزيز تطبيق SSR باستخدام Vue. كان الأمر بسيطًا للغاية. كانت أكبر مشكلة واجهتها هي استبدال Vue لقالب السطر: لم أقرأ الوثائق على نطاق واسع وتجاهلت السمة . is ومع ذلك، كان عليّ كتابة عدد لا بأس به من أسطر JavaScript، على الرغم من أنني استخدمت Axios لمساعدتي في استدعاءات HTTP ولم أتمكن من إدارة الأخطاء. في المنشور التالي، سأقوم بتنفيذ نفس الميزات مع Alpine.js. يمكن العثور على الكود المصدر الكامل لهذه التدوينة على GitHub: https://github.com/ajavageek/compare-frontends?embedable=true اذهب أبعد من ذلك: فيو.جي اس