لقد حان الوقت أخيرًا لجعل تطبيقنا يبدو أجمل قليلًا، إذ سنتعرّف في هذا المقال على الطرق المختلفة لتنسيق مكونات إطار العمل Vue باستخدام لغة CSS، كما سنضيف عدّادًا يعرض عدد عناصر المهام المكتملة باستخدام ميزة في إطار عمل Vue تسمّى الخاصية computed
، إذ تعمل هذه الخاصية بصورة مشابهة للتوابع، ولكن يُعاد تشغيلها فقط عندما تتغير إحدى اعتمادياتها.
- المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript ومعرفة استخدام سطر الأوامر أو الطرفية، إذ تُكتَب مكونات Vue بوصفها مجموعةً من كائنات جافاسكربت التي تدير بيانات التطبيق وصيغة القوالب المستنِدة إلى لغة HTML المرتبطة مع بنية DOM الأساسية، كما ستحتاج إلى طرفية مثبَّت عليها node و npm لتثبيت Vue ولاستخدام بعض ميزاته الأكثر تقدمًا مثل مكونات الملف المفرد Single File Components أو دوال التصيير Render.
-
الهدف: التعرف على مكونات التنسيق وتعلّم كيفية استخدام خاصيات
computed
في Vue.
يجب إضافة شيفرة CSS الأساسية إلى تطبيقنا لجعله يبدو أفضل قبل إضافة مزيد من الميزات المتقدمة إليه، إذ يمتلك إطار العمل Vue ثلاث طرق شائعة لتنسيق التطبيقات:
- ملفات CSS الخارجية.
-
التنسيقات العام في مكونات الملف المفرد، أي الملفات ذات اللاحقة
.vue
. - التنسيقات على مستوى المكونات في مكونات الملف المفرد.
سنستخدِم مزيجًا من الطرق الثلاثة لإضفاء مظهر وتنسيق أفضل على تطبيقنا للتعرف على هذه الطرق.
التنسيق باستخدام ملفات CSS الخارجية
يمكنك تضمين ملفات CSS الخارجية وتطبيقها بطريقة عامة على تطبيقك، لذا أنشئ ملفًا بالاسم reset.css في المجلد src/assets، إذ تُعالَج الملفات الموجودة في هذا المجلد باستخدام Webpack، وبالتالي يمكننا استخدام معالجات CSS المسبقَة مثل SCSS أو معالجات ملحقَة مثل PostCSS. لن نستخدِم في هذا المقال مثل هذه الأدوات، ولكن يجب معرفة أنه ستُعالَج الشيفرة تلقائيًا عند تضمينها في المجلد assets.
أضِف المحتويات التالية في الملف reset.css:
/*reset.css*/ /* إعادة الضبط */ *, *::before, *::after { box-sizing: border-box; } *:focus { outline: 3px dashed #228bec; } html { font: 62.5% / 1.15 sans-serif; } h1, h2 { margin-bottom: 0; } ul { list-style: none; padding: 0; } button { border: none; margin: 0; padding: 0; width: auto; overflow: visible; background: transparent; color: inherit; font: inherit; line-height: normal; -webkit-font-smoothing: inherit; -moz-osx-font-smoothing: inherit; -webkit-appearance: none; } button::-moz-focus-inner { border: 0; } button, input, optgroup, select, textarea { font-family: inherit; font-size: 100%; line-height: 1.15; margin: 0; } button, input { /* 1 */ overflow: visible; } input[type="text"] { border-radius: 0; } body { width: 100%; max-width: 68rem; margin: 0 auto; font: 1.6rem/1.25 "Helvetica Neue", Helvetica, Arial, sans-serif; background-color: #f5f5f5; color: #4d4d4d; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } @media screen and (min-width: 620px) { body { font-size: 1.9rem; line-height: 1.31579; } } /*نهاية إعادة الضبط*/
استورد بعد ذلك الملف reset.css في الملف src/main.js كما يلي:
import './assets/reset.css';
سيؤدي ذلك إلى نقل الملف أثناء خطوة البناء وإضافته تلقائيًا إلى موقعنا.
يجب تطبيق التنسيقات المُعاد ضبطها على التطبيق الآن، إذ تُظهر الصور التالية مظهر التطبيق قبل وبعد تطبيق إعادة الضبط.
قبل إعادة ضبط التنسيق:
بعد إعادة ضبط التنسيق:
تشمل التغييرات الملحوظة إزالة نقط تعداد للقائمة وتغييرات لون الخلفية والتغييرات في الزر الأساسي وتنسيقات حقل الإدخال.
إضافة التنسيقات العامة إلى مكونات الملف المفرد
أعدنا ضبط شيفرة CSS لتكون موحدةًَ في المتصفحات ويجب الآن تخصيص التنسيقات، فهناك بعض التنسيقات التي نريد تطبيقها على مستوى المكونات في تطبيقنا من خلال إضافة التنسيقات إلى وسوم <style>
في الملف App.vue بدلًا من إضافتها مباشرةً إلى ملف التنسيقات reset.css.
توجد مسبقًا بعض التنسيقات في الملف App.vue، فلنحذفها ونستبدلها بالتنسيقات التالية التي تضيف تنسيقًا إلى الأزرار وحقول الإدخال وتخصّص العنصر #app
وأبناءه، لذا عدِّل العنصر <style>
في الملف App.vue بحيث يبدو كما يلي:
<style> /* التنسيقات العامة */ .btn { padding: 0.8rem 1rem 0.7rem; border: 0.2rem solid #4d4d4d; cursor: pointer; text-transform: capitalize; } .btn__danger { color: #fff; background-color: #ca3c3c; border-color: #bd2130; } .btn__filter { border-color: lightgrey; } .btn__danger:focus { outline-color: #c82333; } .btn__primary { color: #fff; background-color: #000; } .btn-group { display: flex; justify-content: space-between; } .btn-group > * { flex: 1 1 auto; } .btn-group > * + * { margin-left: 0.8rem; } .label-wrapper { margin: 0; flex: 0 0 100%; text-align: center; } [class*="__lg"] { display: inline-block; width: 100%; font-size: 1.9rem; } [class*="__lg"]:not(:last-child) { margin-bottom: 1rem; } @media screen and (min-width: 620px) { [class*="__lg"] { font-size: 2.4rem; } } .visually-hidden { position: absolute; height: 1px; width: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); clip-path: rect(1px, 1px, 1px, 1px); white-space: nowrap; } [class*="stack"] > * { margin-top: 0; margin-bottom: 0; } .stack-small > * + * { margin-top: 1.25rem; } .stack-large > * + * { margin-top: 2.5rem; } @media screen and (min-width: 550px) { .stack-small > * + * { margin-top: 1.4rem; } .stack-large > * + * { margin-top: 2.8rem; } } /* نهاية التنسيقات العامة */ #app { background: #fff; margin: 2rem 0 4rem 0; padding: 1rem; padding-top: 0; position: relative; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1); } @media screen and (min-width: 550px) { #app { padding: 4rem; } } #app > * { max-width: 50rem; margin-left: auto; margin-right: auto; } #app > form { max-width: 100%; } #app h1 { display: block; min-width: 100%; width: 100%; text-align: center; margin: 0; margin-bottom: 1rem; } </style>
إذا اختبرت التطبيق، فسترى أنّ قائمة المهام موجودة ضمن قسم خاص به الآن مع بعض التنسيقات الأفضل لعناصر المهام كما يلي:
إضافة أصناف CSS في إطار العمل Vue
سنطبّق أصناف CSS على عنصر الزر <button>
في المكوِّن ToDoForm
، وبما أنّ قوالب Vue تستخدِم لغة HTML، فسنستخدِم الطريقة نفسها عن طريق إضافة السمة class=""
إلى العنصر، لذا أضِف السمة class="btn btn__primary btn__lg"
إلى العنصر <button>
في نموذجك:
<button type="submit" class="btn btn__primary btn__lg"> Add </button>
هناك تغيير آخر يمكن إجراؤه، فبما أنّ النموذج يشير إلى قسم معيّن من الصفحة، فيمكن استخدام العنصر <h2>
، ولكن يشير العنصر label
إلى الغرض من النموذج مسبقًا، لذلك يجب تجنب التكرار من خلال تغليف العنصر label
ضمن العنصر <h2>، كما يمكننا إضافة بعض تنسيقات CSS العامة الأخرى، إذ سنضيف الصنف input__lg
إلى العنصر <input>
، لذا عدِّل قالب المكوّن ToDoForm
بحيث يبدو كما يلي:
<template> <form @submit.prevent="onSubmit"> <h2 class="label-wrapper"> <label for="new-todo-input" class="label__lg"> What needs to be done? </label> </h2> <input type="text" id="new-todo-input" name="new-todo" autocomplete="off" v-model.lazy.trim="label" class="input__lg" /> <button type="submit" class="btn btn__primary btn__lg"> Add </button> </form> </template>
كما سنضيف الصنف stack-large
إلى الوسم <ul>
في الملف App.vue كما يلي، مما يساعد في تحسين التباعد بين عناصر المهام:
<ul aria-labelledby="list-summary" class="stack-large">
إضافة التنسيقات ذات النطاق المحدد
نريد الآن تنسيق المكوِِّن ToDoItem
، إذ يمكننا إضافة العنصر <style>
ضمنه لتكون تعريفات التنسيقات قريبةً من المكوِّن، لكن إذا غيرت هذه التنسيقات أشياءً خارج هذا المكوِّن، فسيكون تعقّب التنسيقات المسؤولة عن ذلك وإصلاح المشكلة أمرًا صعبًا.
سنستخدِم السمة scoped
حيث يرتبط محدِّد سمة HTML الفريدة data
مع جميع التنسيقات، مما يمنعها من التعارض على المستوى العام، كما يمكنك استخدام معدِّل scoped
من خلال إنشاء العنصر <style>
ضمن المكوِّن ToDoItem.vue
، ثم منحه السمة scoped
كما يلي:
<style scoped> </style>
انسخ بعد ذلك شيفرة CSS التالية والصقها في العنصر <style>
الذي أنشأناه للتو:
.custom-checkbox > .checkbox-label { font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-weight: 400; font-size: 16px; font-size: 1rem; line-height: 1.25; color: #0b0c0c; display: block; margin-bottom: 5px; } .custom-checkbox > .checkbox { font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-weight: 400; font-size: 16px; font-size: 1rem; line-height: 1.25; box-sizing: border-box; width: 100%; height: 40px; height: 2.5rem; margin-top: 0; padding: 5px; border: 2px solid #0b0c0c; border-radius: 0; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .custom-checkbox > input:focus { outline: 3px dashed #fd0; outline-offset: 0; box-shadow: inset 0 0 0 2px; } .custom-checkbox { font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; font-weight: 400; font-size: 1.6rem; line-height: 1.25; display: block; position: relative; min-height: 40px; margin-bottom: 10px; padding-left: 40px; clear: left; } .custom-checkbox > input[type="checkbox"] { -webkit-font-smoothing: antialiased; cursor: pointer; position: absolute; z-index: 1; top: -2px; left: -2px; width: 44px; height: 44px; margin: 0; opacity: 0; } .custom-checkbox > .checkbox-label { font-size: inherit; font-family: inherit; line-height: inherit; display: inline-block; margin-bottom: 0; padding: 8px 15px 5px; cursor: pointer; touch-action: manipulation; } .custom-checkbox > label::before { content: ""; box-sizing: border-box; position: absolute; top: 0; left: 0; width: 40px; height: 40px; border: 2px solid currentColor; background: transparent; } .custom-checkbox > input[type="checkbox"]:focus + label::before { border-width: 4px; outline: 3px dashed #228bec; } .custom-checkbox > label::after { box-sizing: content-box; content: ""; position: absolute; top: 11px; left: 9px; width: 18px; height: 7px; transform: rotate(-45deg); border: solid; border-width: 0 0 5px 5px; border-top-color: transparent; opacity: 0; background: transparent; } .custom-checkbox > input[type="checkbox"]:checked + label::after { opacity: 1; } @media only screen and (min-width: 40rem) { label, input, .custom-checkbox { font-size: 19px; font-size: 1.9rem; line-height: 1.31579; } }
يجب الآن إضافة أصناف CSS إلى القالب لربط التنسيقات، لذا انتقل إلى العنصر <div> الجذر ثم أضِف الصنف custom-checkbox
، وأضف الصنف checkbox
إلى العنصر <input>
ثم أضف الصنف إلى العنصر <label>
، وبالتالي سيحتوي التطبيق على مربعات اختيار مخصَّصة، كما يجب أن يبدو تطبيقك الآن كما يلي:
انتهينا من تنسيق تطبيقنا، وسنعود الآن إلى إضافة مزيد من الوظائف إليه، أي استخدام الخاصية computed
لإضافة عدد عناصر المهام المكتملة إلى تطبيقنا.
استخدام الخاصية computed في إطار العمل Vue
نريد إضافة عدّاد counter يلخّص عدد العناصر في قائمة المهام، مما يساعد التقنيات المساعدة في معرفة عدد المهام المكتملة، فإذا كان لدينا عنصران مكتملان من أصل خمسة في قائمة المهام، فسنرى العبارة "2 items completed out of 5" كما يلي:
<h2>{{ToDoItems.filter(item => item.done).length}} out of {{ToDoItems.length}} items completed</h2>
سيُعاد حساب هذا العدد في كل عملية تصيير، ويمكن أن يكون ذلك غير مهم بالنسبة إلى تطبيق صغير مثل تطبيقنا، ولكنه سيتسبب في مشكلة خطيرة في الأداء بالنسبة للتطبيقات الأكبر حجمًا أو عندما يكون التعبير أكثر تعقيدًا، لذا يكون الحل الأفضل هو استخدام خاصيات computed
في إطار العمل Vue، إذ تعمل هذه الخاصيات بصورة مشابهة للتوابع، ولكن يُعاد تشغيلها فقط عندما تتغير إحدى اعتمادياتها، إذ سيُعاد تشغيلها في حالتنا عندما تتغير المصفوفة ToDoItems
فقط.
يمكن إنشاء الخاصية computed
من خلال إضافتها إلى كائن المكوِّن مثل الخاصية methods
التي استخدمناها سابقًا.
إضافة عداد
أضِف الشيفرة البرمجية التالية إلى كائن المكوِّن App
بعد الخاصية methods
، إذ يأخذ التابع listSummary()
عدد عناصر ToDoItems
المنتهية، ويعيد سلسلةً نصيةً تمثّل ذلك.
computed: { listSummary() { const numberFinishedItems = this.ToDoItems.filter(item =>item.done).length return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed` } }
يمكننا الآن إضافة {{listSummary}}
مباشرةً إلى نموذجنا، إذ سنضيفه ضمن العنصر <h2>
قبل العنصر <ul>
مباشرةً، كما سنضيف السمة id
والسمة aria-labelledby
لتكون محتويات العنصر <h2>
عنوانًا label
للعنصر <ul>
، لذا أضِف العنصر <h2>
وعدّل العنصر <ul>
ضمن قالب المكون App
كما يلي:
<h2 id="list-summary">{{listSummary}}</h2> <ul aria-labelledby="list-summary" class="stack-large"> <li v-for="item in ToDoItems" :key="item.id"> <to-do-item :label="item.label" :done="item.done" :id="item.id"></to-do-item> </li> </ul>
يجب أن تشاهد الآن ملخص القائمة في تطبيقك مع تحديث عدد العناصر الإجمالي كلما أضفت مزيدًا من عناصر المهام، ولكن إذا حاولت تحديد بعض العناصر ثم إلغاء تحديدها، فسيظهر خطأ لأننا لم نتعقّب بعد البيانات "المكتملة" فعليًا، لذلك لن يتغير عدد العناصر المكتملة.
تعقب تغيرات عدد العناصر المكتملة
يمكننا استخدام الأحداث لالتقاط تحديث مربع الاختيار وإدارة القائمة وفقًا لذلك، كما يمكننا ربط معالج الحدث @change
مع كل مربع اختيار بدلًا من استخدام الموجِّه v-model
لأننا لا نعتمد على ضغط الزر لبدء التغيير.
عدّل العنصر <input>
في المكون ToDoItem.vue
ليبدو كما يلي، كما يمكننا تضمين $emit()
لأنّ كل ما يجب فعله هو إرسال أن مربع الاختيار محدَّد فقط:
<input type="checkbox" class="checkbox" :id="id" :checked="isDone" @change="$emit('checkbox-changed')" />
أضف تابعًا جديدًا بالاسم updateDoneStatus()
في المكوِّن App.vue
بعد التابع addToDo()
، إذ يجب أن يأخذ هذا التابع معامِلًا واحدًا هو معرّف عنصر المهمة، كما نريد العثور على العنصر ذي المعرّف id
المطابق وتحديث حالته done
لتكون معاكسةً لحالته الحالية:
updateDoneStatus(toDoId) { const toDoToUpdate = this.ToDoItems.find(item => item.id === toDoId) toDoToUpdate.done = !toDoToUpdate.done }
يجب تشغيل هذا التابع كلما أصدر المكوِّن ToDoItem
الحدث checkbox-changed
، وتمرير معرّفه item.id
بوصفه معاملًا، لذا عدّل الاستدعاء <to-do-item></to-do-item>
كما يلي:
<to-do-item :label="item.label" :done="item.done" :id="item.id" @checkbox-changed="updateDoneStatus(item.id)"> </to-do-item>
إذا اختبرت العنصر ToDoItem
الآن، فيجب أن تشاهد تحديث العدّاد الملخِّص بطريقة صحيحة.
الخلاصة
تعرّفنا في هذا المقال على تنسيق مكوّنات إطار العمل Vue باستخدام لغة CSS، واستخدمنا الخاصية computed
لإضافة ميزة مفيدة إلى تطبيقنا وهي حساب عدد عناصر المهام المكتملة، وسنتعرّف في المقال التالي على التصيير أو العرض الشرطي Conditional Rendering وكيفية استخدامه لإظهار نموذج التعديل عندما نريد تعديل عناصر المهام الحالية.
ترجمة -وبتصرّف- للمقالين Styling Vue components with CSS وUsing Vue computed properties.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.