اقتربنا من نهاية سلسلة إطار العمل Vue.js، إذ يجب الآن تعلّم كيفية الوصول إلى العناصر وإدارتها وتعديلها مثل إدارة التركيز أو كيف يمكننا تحسين الشمولية أو إمكانية الوصول لمستخدمي لوحة المفاتيح في تطبيقنا، إذ سنتعرّف على كيفية استخدام خاصيات ref
في إطار العمل Vue للتعامل مع إدارة التركيز التي تُعَدّ ميزةً متقدمةً تتيح الوصول المباشر إلى عقد DOM الأساسية أسفل نموذج DOM الافتراضي أو الوصول المباشر من أحد المكوّنات إلى بنية DOM الداخلية الخاصة بمكوِّن ابن، كما سنوفِّر مزيدًا من الموارد لتعلّم إطار عمل Vue للاستزادة منها.
- المتطلبات الأساسية: الإلمام بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة استخدام سطر الأوامر أو الطرفية، إذ تُكتَب مكوّنات Vue بوصفها مجموعةً من كائنات جافاسكربت التي تدير بيانات التطبيق وصيغة القوالب المستنِدة إلى لغة HTML المرتبطة مع بنية DOM الأساسية، كما ستحتاج إلى طرفية مثبَّتٌ عليها node و npm لتثبيت Vue ولاستخدام بعض ميزاته الأكثر تقدمًا مثل مكونات الملف المفرد Single File Components أو دوال التصيير Render.
-
الهدف: تعلّم كيفية التعامل مع إدارة التركيز باستخدام خاصيات
ref
في إطار العمل Vue.
مشكلة إدارة التركيز
لدينا وظيفة تعديل تعمل بصورة جيدة في تطبيقنا، إلا أننا لم نقدّم تجربةً رائعةً للمستخدِمين الذين لا يستخدِمون الفأرة، فإذا فعّل المستخدِم زر "التعديل Edit"، فإننا نزيل هذا الزر من نموذج DOM دون نقل تركيز المستخدِم إلى أيّ مكان آخر، لذلك سيختفي التركيز، ويمكن أن يكون هذا مربكًا لمستخدِمي لوحة المفاتيح والمستخدِمين غير المبصرين.
طبقّ ما يلي لفهم ما يحدث حاليًا:
-
أعد تحميل صفحتك ثم اضغط على مفتاح
Tab
، ويجب أن تشاهد إطارًا يمثل التركيز حول حقل الإدخال لإضافة عناصر مهام جديدة. -
اضغط على مفتاح
Tab
مرةً أخرى، ويجب أن ينتقل التركيز إلى زر "الإضافة Add". -
اضغط على مفتاح
Tab
مرةً أخرى وسينتقل التركيز إلى مربع الاختيار الأول، ثم يجب أن ينتقل التركيز إلى زر "التعديل Edit" الأول. -
فعّل زر "التعديل Edit" بالضغط على مفتاح
Enter
، وبالتالي سيُستبدَل مربع الاختيار بمكوِّن التعديل، ولكن سيختفي إطار التركيز.
يمكن أن يكون هذا السلوك مربكًا، ويختلف ما يحدث عند الضغط على مفتاح Tab
مرةً أخرى تبعًا للمتصفح الذي تستخدِمه، فإذا حفظت التعديل أو ألغيته، فسيختفي التركيز مرةً أخرى عندما تعود إلى العرض الذي لا يتضمن تعديلًا.
يمكن منح المستخدِمين تجربةً أفضل من خلال إضافة شيفرة برمجية للتحكّم في التركيز بحيث يُضبَط على حقل التعديل عند عرض نموذج التعديل، وسنعيد وضع التركيز على زر "التعديل Edit" عندما يلغي المستخدِم تعديله أو يحفظه، وبالتالي يجب فهم المزيد حول كيفية عمل إطار Vue داخليًا بهدف ضبط التركيز.
نموذج DOM الافتراضي وخاصيات ref
يستخدِم إطار العمل Vue مثل بعض أطر العمل الأخرى نموذج DOM الافتراضي Virtual DOM -أو VDOM اختصارًا- لإدارة العناصر، وهذا يعني أنّ إطار Vue يحتفظ في الذاكرة بتمثيلٍ لجميع العقد في تطبيقنا، كما يمكن إجراء أيّ تحديثات أولًا على العقد الموجودة في الذاكرة، ثم تُزامَن جميع التغييرات التي يجب إجراؤها على العقد الفعلية على الصفحة دفعةً واحدةً.
بما أن قراءة وكتابة عقد DOM الفعلية تكون مستهلكة أكثر للأداء من العقد الافتراضية في أغلب الأحيان، فسيؤدي ذلك إلى أداء أفضل، ولكنه يعني أيضًا أنه لا يجب تعديل عناصر HTML مباشرةً من خلال واجهات برمجة تطبيقات المتصفح الأصيلة Native مثل Document.getElementById
عند استخدام أطر العمل، لأنه سيؤدي إلى عدم مزامنة نموذج VDOM و DOM الفعلي، فإذا كنت بحاجة إلى الوصول إلى عقد DOM الأساسية مثل حالة ضبط التركيز، فيمكنك استخدام خاصيات ref في إطار Vue، كما يمكنك بالنسبة لمكّونات Vue المخصَّصة استخدام خاصيات ref للوصول مباشرةً إلى البنية الداخلية لمكوّن ابن، ولكن يجب تطبيق ذلك بحذر لأنه قد يصعّب فهم الشيفرة البرمجية.
يمكن استخدام خاصيات ref في أحد المكونات من خلال إضافة السمة ref
إلى العنصر الذي تريد الوصول إليه مع معرِّف من نوع سلسلة نصية لقيمة هذه السمة، إذ يجب أن تكون السمة ref
فريدةً في المكون، ولا ينبغي أن يكون هناك عنصران يصيّّران في الوقت نفسه ولهما السمة ref
نفسها.
إضافة الخاصية ref إلى التطبيق
لنضِف السمة ref
إلى زر "التعديل Edit" في المكوِّن ToDoItem.vue
كما يلي:
<button type="button" class="btn" ref="editButton" @click="toggleToItemEditForm"> Edit <span class="visually-hidden">{{label}}</span> </button>
يمكن الوصول إلى القيمة المرتبطة بالسمة ref
من خلال استخدام الخاصية $refs
المتوفِّرة في نسخة المكوِّن، لذا أضِف التابع console.log()
إلى التابع toggleToItemEditForm()
كما يلي لمعرفة قيمة السمة ref
عند النقر على زر "التعديل Edit":
toggleToItemEditForm() { console.log(this.$refs.editButton); this.isEditing = true; }
إذا فعّلتَ زر "التعديل Edit" الآن، فيُفترَض أن ترى عنصر الزر <button> في HTML مشارًا إليه في طرفيتك.
التابع $nextTick() في إطار العمل Vue
يجب الآن التركيز على زر "التعديل Edit" عندما يحفظ المستخدِم التعديل أو يلغيه، لذلك يجب التعامل مع التركيز باستخدام التابعَين itemEdited()
و editCancelled()
في المكوِّن ToDoItem
.
أنشئ تابعًا جديدًا لا يأخذ أيّ وسيط بالاسم focusOnEditButton()
وأسند السمة ref
إلى متغير، ثم استدعِ التابع focus()
على هذه السمة.
focusOnEditButton() { const editButtonRef = this.$refs.editButton; editButtonRef.focus(); }
أضِف بعد ذلك استدعاءً إلى this.focusOnEditButton()
في نهاية التابعَين itemEdited()
و editCancelled()
كما يلي:
itemEdited(newItemName) { this.$emit("item-edited", newItemName); this.isEditing = false; this.focusOnEditButton(); }, editCancelled() { this.isEditing = false; this.focusOnEditButton(); },
جرّب تعديل عنصر مهمة ثم حفظ التعديل أو إلغاءه باستخدام لوحة المفاتيح وستلاحظ عدم ضبط التركيز، لذلك لا تزال لدينا مشكلة ويجب حلها، فإذا فتحتَ طرفيتك، فسترى ظهور الخطأ الذي يبدو غريبًا:
"can't access property "focus", editButtonRef is undefined"
إذ جرى تعريف المرجع ref الخاص بالزر عند تفعيل زر "التعديل Edit"، ولكنه ليس كذلك الآن، وتذكّر أننا لم نعُد نصيّر قسم المكوّن الذي يحتوي على زر "التعديل Edit" عند تغيير قيمة isEditing
إلى true
، وبالتالي لا يوجد عنصر لربط المرجع به، لذلك يصبح غير مُعرَّف.
لكننا نضبط قيمة isEditing
على false
قبل محاولة الوصول إلى المرجع، فهل يجب أن يعرض الموجّه v-if
الزر الآن؟ حسنًا، يجب الآن استخدام نموذج DOM الافتراضي، وبما أنّ Vue يحاول تحسين وإصلاح التغييرات، فلن يحدّث نموذج DOM مباشرةً عند ضبط قيمة isEditing
على false
، لذلك فإنّ زر "التعديل Edit" لم يُصيَّر بعد عند استدعاء التابع focusOnEdit()
، لذا يجب الانتظار حتى يجتاز إطار Vue دورة تحديث نموذج DOM التالية، إذ تحتوي مكونات Vue على تابع خاص يسمَّى $nextTick()
الذي يقبل دالة رد نداء Callback Function تنفّذ بعد تحديث نموذج DOM.
يمكننا تغليف جسم الدالة الحالية ضمن استدعاء الدالة $nextTick()
لأنه يجب استدعاء التابع focusOnEditButton()
بعد تحديث نموذج DOM.
focusOnEditButton() { this.$nextTick(() => { const editButtonRef = this.$refs.editButton; editButtonRef.focus(); }); }
إذا فعّلتَ زر "التعديل Edit" ثم ألغيت التغييرات أو حفظتها باستخدام لوحة المفاتيح، فيجب إعادة التركيز إلى زر "التعديل Edit".
توابع دورة حياة إطار عمل Vue
تتمثَّل الخطوة التالية في نقل التركيز إلى العنصر <input> في نموذج التعديل عند النقر على زر "التعديل Edit"، ولكن لا يمكننا فقط وضع التركيز ضمن معالِج حدث النقر على زر "التعديل Edit" لأن نموذج التعديل موجود في مكوِّن مختلف عن هذا الزر، لذا يمكننا إزالة المكوِّن ToDoItemEditForm
ونعيد تحميله كلما نقرنا على زر "التعديل Edit" للتعامل مع هذا الأمر.
تجتاز مكوّنات Vue سلسلةً من الأحداث تُعرَف باسم دورة الحياة Lifecycle التي تمتد من قبل إنشاء العناصر وإضافتها إلى نموذج VDOM أي تثبيتها، حتى إزالتها من نموذج VDOM أي تدميرها.
يتيح إطار عمل Vue تشغيل توابع في مراحل مختلفة من دورة الحياة باستخدام توابع دورة الحياة Lifecycle Methods المفيدة لأشياء متعددة مثل جلب البيانات، إذ يمكن أن تحتاج إلى الحصول على بياناتك قبل تصيير مكوِّنك أو بعد تغيير الخاصيات، وتمثِّل القائمة التالية قائمة توابع دورة الحياة حسب الترتيب الذي تُطلَق فيه:
-
beforeCreate()
: يُشغَّل قبل إنشاء نسخة من مكوِّنك، وليست البيانات والأحداث متاحةً بعد. -
created()
: يُشغَّل بعد تهيئة مكوِّنك ولكن قبل إضافته إلى نموذج VDOM، ويُعَدّ المكان الذي يحدث فيه جلب البيانات. -
beforeMount()
: يُشغَّل بعد تصريف Compile قالبك وقبل تصيير مكوِّنك إلى نموذج DOM الفعلي. -
mounted()
: يُشغَّل بعد تثبيت مكوِّنك في نموذج DOM، ويمكن هنا الوصول إلى المراجعrefs
. -
beforeUpdate()
: يُشغَّل كلما تغيرت البيانات في مكوِّنك وقبل تصيير التغييرات إلى نموذج DOM. -
updated()
: يُشغَّل كلما تغيرت البيانات في مكوِّنك وبعد تصيير التغييرات إلى نموذج DOM. -
beforeDestroy()
: يُشغَّل قبل إزالة أحد المكوّنات من نموذج DOM. -
destroyed()
: يُشغَّل بعد إزالة أحد المكونات من نموذج DOM. -
activated()
: يُستخدَم فقط في المكونات المغلَّفة بوسمkeep-alive
الخاص، ويعمل بعد تفعيل المكون. -
deactivated()
: يُستخدَم فقط في المكونات المغلَّفة بوسمkeep-alive
الخاص، ويعمل بعد إلغاء تفعيل المكون.
ملاحظة: يمكنك الاطلاع على مخطط رائع يشرح وقت حدوث هذه التوابع في توثيق Vue.
لنستخدِم الآن تابعًا من توابع دورة الحياة لبدء التركيز عند تثبيت المكوِّن ToDoItemEditForm
، لذا أضِف ref="labelInput"
إلى العنصر <input>
في المكوِّن ToDoItemEditForm.vue
كما يلي:
<input :id="id" ref="labelInput" type="text" autocomplete="off" v-model.lazy.trim="newName" />
أضف بعد ذلك الخاصية mounted()
إلى كائن المكوِّن مباشرةً، إذ لا ينبغي وضعها ضمن الخاصية methods
، وإنما يجب وضعها في مستوى تسلسل props
و data()
و methods
الهرمي نفسه، فتوابع دورة الحياة هي توابع خاصة بمفردها ولا توضَع مع التوابع التي يعرّفها المستخدِم ولا تأخذ أيّ مدخلات، ولاحظ أنه لا يمكنك استخدام دالة سهمية هنا لأننا نحتاج الوصول إلى this
للوصول إلى المرجع labelInput
.
mounted() { }
أسنِد المرجع labelInput
إلى متغير ضمن التابع mounted()
، ثم استدعِ الدالة focus()
الخاصة بالمرجع، ولست مضطرًا إلى استخدام $nextTick
هنا لأن المكوِّن مُضاف بالفعل إلى نموذج DOM عند استدعاء التابع mounted()
.
mounted() { const labelInputRef = this.$refs.labelInput; labelInputRef.focus(); }
إذا فعّلتَ زر "التعديل Edit" الآن باستخدام لوحة المفاتيح، فيجب أن ينتقل التركيز إلى تعديل العنصر <input>
مباشرةً.
التعامل مع التركيز عند حذف عناصر المهام
إذا نقرت على زر "التعديل Edit"، فسيكون نقل التركيز إلى مربع نص تعديل الاسم والعودة إلى زر "التعديل Edit" عند الإلغاء أو الحفظ من نموذج التعديل أمرًا منطقيًا، لكن ليس لدينا موقع واضح لنقل التركيز إليه عند حذف عنصر، ونحتاج إلى طريقة لتزويد مستخدِمي التقنيات المساعدة بالمعلومات التي تؤكّد حذف عنصر.
نتعقّب فعليًا عدد العناصر في عنوان القائمة أي العنصر <h2> في المكوِّن App.vue
، وهو مرتبط بقائمة عناصر المهام، مما يجعله مكانًا مناسبًا لنقل التركيز إليه عند حذف عقدة.
يجب أولًا إضافة مرجع إلى عنوان القائمة ثم إضافة السمة tabindex="-1"
إليه، مما يجعل العنصر قابلًا للتركيز برمجيًا، أي يمكن التركيز عليه باستخدام شيفرة جافاسكربت إن لم يكن كذلك افتراضيًا.
عدِّل العنصر <h2>
في المكوِّن App.vue
كما يلي:
<h2 id="list-summary" ref="listSummary" tabindex="-1">{{listSummary}}</h2>
ملاحظة: تُعَدّ السمة tabindex
أداةً قويةً للتعامل مع بعض مشاكل الشمولية Accessibility، ولكن يجب استخدامها بحذر، إذ يمكن أن يتسبّب الإفراط في استخدام السمة tabindex="-1"
في حدوث مشاكل لجميع أنواع المستخدِمين، لذلك استخدمها فقط في المكان الذي تحتاجها فيه فقط، كما يجب عدم استخدام السمة tabindex > = 0
، إذ يمكن أن تسبب مشاكل للمستخدِمين لأنها يمكن أن تؤدي إلى عدم التطابق مع ترتيب انتقال التركيز باستخدام مفتاح Tab
في نموذج DOM، و / أو إضافة عناصر غير تفاعلية إلى هذا الترتيب، كما يكون ذلك مربكًا للمستخدِمين خاصةً أولئك الذين يستخدِمون قارئات الشاشة والتقنيات المساعدة الأخرى.
أصبح لدينا مرجع ref
وأعلَمنا المتصفحات أنه يمكننا التركيز برمجيًا على العنصر <h2>
، ويجب الآن التركيز عليه.
استخدم المرجع listSummary
في نهاية التابع deleteToDo()
لضبط التركيز على العنصر <h2>
، وبما أنّ العنصر <h2>
يُصيَّر دائمًا في التطبيق، فليس هناك داع للقلق بشأن استخدام $nextTick
مع توابع دورة الحياة للتعامل مع التركيز عليه.
deleteToDo(toDoId) { const itemIndex = this.ToDoItems.findIndex(item => item.id === toDoId); this.ToDoItems.splice(itemIndex, 1); this.$refs.listSummary.focus(); }
إذا حذفتَ عنصرًا من قائمتك الآن، فيجب نقل التركيز إلى عنوان القائمة الذي يجب أن يوفِّر تجربة تركيز جيدة لجميع المستخِدمين.
تهانينا، انتهينا الآن من إنشاء تطبيقنا باستخدام إطار العمل Vue، وسنتعرّف الآن على بعض الموارد الإضافية لتعلم إطار العمل Vue.
ملاحظة: إذا كنت بحاجة إلى التحقق من شيفرتك مقابل نسختنا، فيمكنك العثور على نسخة نهائية من شيفرة تطبيق Vue في مستودع todo-vue، كما يمكنك الحصول على إصدار حي مباشر قيد التشغيل.
موارد إضافية لتعلم إطار العمل Vue
سنختتم الآن هذا القسم من سلسلة تعلم تطوير الويب الذي يشمل إطار العمل Vue من خلال إعطائك قائمة بالموارد التي يمكنك استخدامها في مسيرة تعلمك، بالإضافة إلى بعض النصائح المفيدة الأخرى.
من الموارد التي يجب أن تتطلع عليها لمعرفة المزيد عن إطار العمل Vue:
- كتاب أساسيات إطار العمل Vue .js من أكاديمية حسوب الذي يشرح مفهوم إطار العمل Vue.
- مقالات عن Vue: مقالات عربية من أكاديمية حسوب عن إطار العمل Vue.js.
- قسم الأسئلة البرمجية: إن أردت الحصول على مساعدة من مبرمجين عرب.
- منتدى البرمجة العربية: المنتدى البرمجي العربي للحصول على مساعدة في إطار العمل Vue أو أي شيء متعلق بالبرمجة.
- توثيق Vue: يحتوي موقع Vue الرسمي على توثيق شامل بما في ذلك الأمثلة والكتب والموارد المرجعية.
- مستودع Vue Github: شيفرة Vue البرمجية نفسها حيث يمكنك فيه الإبلاغ عن المشاكل والمساهمة مباشرةً في شيفرة Vue الأساسية، كما يمكن أن تساعدك دراسة شيفرة Vue البرمجية على فهم كيفية عمل إطار العمل وكتابة شيفرة أفضل.
- توثيق Vue CLI: يحتوي على معلومات حول تخصيص وتوسيع الخرج الذي تنشئه باستخدام واجهة سطر الأوامر CLI.
- NuxtJS: هو إطار عمل من Vue من طرف الخادم مع بعض الآراء المعمارية التي يمكن أن تكون مفيدةً لإنشاء تطبيقات قابلة للصيانة، حتى إن لم تستخدِم أيًا من ميزات التصيير من طرف الخادم Server Side Rendering التي يوفرها، كما يوفِّر توثيقًا مفصلًا حول استخدام NuxtJS.
بناء ونشر تطبيق Vue
توفِّر واجهة CLI في Vue أدوات لإعداد تطبيقنا للنشر على الويب كما يلي:
-
إذا كان خادمك المحلي لا يزال قيد التشغيل، فيمكن إنهاؤه بالضغط على الاختصار
Ctrl + C
في الطرفية. -
شغّل الأمر
npm run build
أو الأمرyarn build
في الطرفية.
مما يؤدي إلى إنشاء مجلد dist
جديد يحتوي على جميع ملفات الإنتاج الجاهزة. يمكنك نشر موقعك على الويب من خلال نسخ محتويات هذا المجلد إلى بيئة استضافتك.
ملاحظة: يتضمن توثيق Vue CLI دليلًا حول كيفية نشر تطبيقك على العديد من أنظمة الاستضافة الشائعة.
إصدار Vue 3
يُعَدّ Vue 3 إصدارًا رئيسيًا من Vue مع الكثير من التغييرات الرئيسية، وقد دخل هذا الإصدار مرحلة الإصدار التجريبي النشط في شهر 4 من عام 2020، كما يُعَدّ أكبر تغيير فيه هو واجهة Composition API الجديدة التي تعمل بوصفها بديلًا لواجهة API الحالية القائمة على الخاصيات، وتُستخدَم دالة setup()
واحدة مع المكوِّن في هذه الواجهة الجديدة، حيث يتوفر فقط ما تعيده من هذه الدالة في عناصر القوالب <template>
. كما يجب أن تكون واضحًا بشأن الخاصيات التفاعلية عند استخدام هذه الواجهة، إذ يعالج إطار العمل Vue ذلك نيابةً عنك باستخدام الواجهة Options API، مما يجعل واجهة برمجة التطبيقات الجديدة حالة استخدام أكثر تقدمًا.
كما توجد بعض التغييرات الأخرى، بما في ذلك التغيير في كيفية تهيئة التطبيقات في Vue، ويمكنك الاطلاع عليها في توثيق Vue.js الرسمي.
ترجمة -وبتصرّف- للمقالَين Focus management with Vue refs وVue resources.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.