أضفنا في مقال تقسيم تطبيق Svelte إلى مكونات من هذه السلسلة مزيدًا من الميزات إلى قائمة المهام وبدأنا بتنظيم تطبيقنا ضمن مكونات، وسنضيف في هذا المقال الميزات النهائية لتطبيقنا مع استكمال تقسيمه إلى مكونات، وسنتعلم كيفية التعامل مع مشاكل التفاعل المتعلقة بتحديث الكائنات والمصفوفات، كما سنتعرّف على حل بعض مشاكل تركيز سهولة الوصول أو الشمولية أي سهولة وصول كل المستخدِمين خصوصًا من يملك بعض الإعاقات وغير ذلك.
- المتطلبات الأساسية: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات HTML وCSS وجافاسكربت JavaScript، ومعرفة باستخدام سطر الأوامر أو الطرفية، وستحتاج طرفية مثبَّت عليها node و npm لتصريف وبناء تطبيقك.
- الهدف: تعلّم بعض تقنيات Svelte المتقدمة التي تتضمن حل مشاكل التفاعل ومشاكل سهولة الوصول لمستخدِمي لوحة المفاتيح المتعلقة بدورة حياة المكونات وغير ذلك.
سنركز على بعض مشاكل سهولة الوصول التي تتضمن إدارة التركيز، إذ سنستخدِم بعض التقنيات للوصول إلى عُقد نموذج DOM وتنفيذ توابع مثل التابعَين focus()
و select()
، كما سنرى كيفية التصريح عن تنظيف مستمعي الأحداث على عناصر DOM، كما سنتعلّم بعض الأمور عن دورة حياة المكونات لفهم متى تُثبَّت عُقد DOM ومتى تُفصَل من نموذج DOM وكيف يمكننا الوصول إليها، كما سنتعرف على الموجه action
الذي سيسمح بتوسيع وظائف عناصر HTML بطريقة قابلة لإعادة الاستخدام والتصريح.
أخيرًا، سنتعلم المزيد عن المكونات، فقد رأينا سابقًا كيف يمكن للمكونات مشاركة البيانات باستخدام الخاصيات Props والتواصل مع المكونات الآباء باستخدام الأحداث وربط البيانات ثنائي الاتجاه، وسنرى الآن كيف يمكن للمكونات الوصول إلى التوابع والمتغيرات.
سنطوّر المكونات الجديدة التالية خلال هذا المقال:
-
MoreActions
: يعرض الزرين "تحديد الكل Check All" و"حذف المهام المكتملة Remove Completed" ويصدر الأحداث المقابلة المطلوبة للتعامل مع وظائفهما. -
NewTodo
: يعرض حقل الإدخال<input>
وزر "الإضافة Add" لإضافة مهمة جديدة. -
TodosStatus
: عرض عنوان الحالة "x out of y items completed" التي تمثِّل المهام المكتملة.
يمكن متابعة كتابة شيفرتك معنا، لذلك انسخ أولًا مستودع github -إذا لم تفعل ذلك مسبقًا- باستخدام الأمر التالي:
git clone https://github.com/opensas/mdn-svelte-tutorial.git
ثم يمكنك الوصول إلى حالة التطبيق الحالية من خلال تشغيل الأمر التالي:
cd mdn-svelte-tutorial/05-advanced-concepts
أو يمكنك تنزيل محتوى المجلد مباشرةً كما يلي:
npx degit opensas/mdn-svelte-tutorial/05-advanced-concepts
تذكَّر تشغيل الأمر npm install && npm run dev
لبدء تشغيل تطبيقك في وضع التطوير، فإذا أردت متابعتنا، فابدأ بكتابة الشيفرة باستخدام الأداة REPL من svelte.dev.
المكون MoreActions
سنعالج الآن الزرين "تحديد الكل Check All" و"حذف المهام المكتملة Remove Completed"، لذا لننشئ مكونًا يكون مسؤولًا عن عرض الأزرار وإصدار الأحداث المقابلة.
أولًا، أنشئ ملفًا جديدًا بالاسم components/MoreActions.svelte
.
ثانيًا، سنرسل الحدث checkAll
عند النقر على الزر الأول للإشارة إلى أنه يجب تحديد أو إلغاء تحديد جميع المهام، كما سنرسل الحدث removeCompleted
عند النقر على الزر الثاني للإشارة إلى أنه يجب حذف جميع المهام المكتملة، لذا ضَع المحتوى التالي في الملف MoreActions.svelte:
<script> import { createEventDispatcher } from 'svelte' const dispatch = createEventDispatcher() let completed = true const checkAll = () => { dispatch('checkAll', completed) completed = !completed } const removeCompleted = () => dispatch('removeCompleted') </script> <div class="btn-group"> <button type="button" class="btn btn__primary" on:click={checkAll}>{completed ? 'Check' : 'Uncheck'} all</button> <button type="button" class="btn btn__primary" on:click={removeCompleted}>Remove completed</button> </div>
ضمّنا المتغير completed
للتبديل بين تحديد جميع المهام وإلغاء تحديدها.
ثالثًا، سنستورد المكوِّن MoreActions
مرةً أخرى في Todos.svelte
وسننشئ دالتين للتعامل مع الأحداث الصادرة من المكوِّن MoreActions
، لذا أضف تعليمة الاستيراد التالية بعد تعليمات الاستيراد الموجودة مسبقًا:
import MoreActions from './MoreActions.svelte'
رابعًا، أضف بعد ذلك الدوال الموضَّحة في نهاية القسم <script>
:
const checkAllTodos = (completed) => todos.forEach((t) => t.completed = completed) const removeCompletedTodos = () => todos = todos.filter((t) => !t.completed)
خامسًا، انتقل الآن إلى الجزء السفلي من شيفرة HTML الخاصة بـ Todos.svelte
واستبدل العنصر <div> الذي له الصنف btn-group
والذي نسخناه إلى MoreActions.svelte باستدعاء المكوِّن MoreActions
كما يلي:
<!-- MoreActions --> <MoreActions on:checkAll={e => checkAllTodos(e.detail)} on:removeCompleted={removeCompletedTodos} />
سادسًا، لنعد إلى التطبيق ونجربه، إذ ستجد أنّ زر "حذف المهام المكتملة Remove Completed" يعمل بصورة جيدة، ولكن يفشل الزر "تحديد الكل Check All" أو "إلغاء تحديد الكل Uncheck All".
اكتشاف التفاعل: تحديث الكائنات والمصفوفات
يمكننا تسجيل المصفوفة todos
من الدالة checkAllTodos()
إلى الطرفية لمعرفة ما يحدث.
أولًا، عدّل الدالة checkAllTodos()
إلى ما يلي:
const checkAllTodos = (completed) => { todos.forEach((t) => t.completed = completed); console.log('todos', todos); }
ثانيًا، ارجع إلى متصفحك وافتح طرفية أدوات التطوير DevTools وانقر على زر تحديد الكل أو إلغاء تحديد الكل عدة مرات.
ستلاحظ أنّ المصفوفة تُحدَّث بنجاح في كل مرة تضغط فيها على الزر، إذ تُبدَّل الخاصيات completed
الخاصة بالكائنات todo
بين القيمتين true
و false
، ولكن إطار Svelte ليس على علم بذلك، وهذا يعني أنه لن تكون تعليمة التفاعل مثل التعليمة $: console.log('todos', todos)
مفيدةً جدًا في هذه الحالة، لذلك يجب فهم كيفية عمل التفاعل في إطار Svelte عند تحديث المصفوفات والكائنات.
تستخدِم العديد من أطر عمل الويب تقنية نموذج DOM الافتراضي لتحديث الصفحة، إذ يُعَدّ DOM الافتراضي نسخةً في الذاكرة لمحتويات صفحة الويب، كما يحدّث إطار العمل هذا التمثيل الافتراضي الذي تجري مزامنته بعد ذلك مع نموذج DOM الحقيقي، وهذا أسرع بكثير من التحديث المباشر لنموذج DOM ويسمح لإطار العمل بتطبيق العديد من تقنيات التحسين، إذ تعيد هذه الأطر تشغيل كل شيفرة جافاسكربت افتراضيًا في كل تغيير لنموذج DOM الافتراضي، وتطبّق توابعًا مختلفةً لتخزين العمليات الحسابية باهظة الثمن مؤقتًا ولتحسين التنفيذ.
لا يستخدِم إطار Svelte تمثيل نموذج DOM الافتراضي، وإنما يحلّل الشيفرة وينشئ شجرةً اعتماديةً، ثم ينشئ شيفرة جافاسكربت المطلوبة لتحديث أجزاء نموذج DOM التي تحتاج إلى تحديث فقط، إذ تنشئ هذه التقنية شيفرة جافاسكربت مثالية بأقل قدر من عمليات المعالجة إلى حد ما ولكن لذلك لا يخلو من بعض القيود.
يتعذر على إطار Svelte في بعض الأحيان اكتشاف التغييرات التي تطرأ على المتغيرات المُراقَبة، وتذكَّر أنه يمكنك إخبار إطار Svelte بتغيّر متغير ما من خلال إسناد قيمة جديدة إليه، كما يجب أن يظهر اسم المتغير المُحدَّث على الجانب الأيسر من هذا الإسناد كما يلي على سبيل المثال:
const foo = obj.foo foo.bar = 'baz'
لن يحدِّث إطار عمل Svelte مراجع الكائن obj.foo.bar
إلّا إذا تتبّعتها باستخدام الإسناد obj = obj
، إذ لا يمكن لإطار عمل Svelte تتبّع مراجع الكائنات، لذلك يجب إخباره صراحةً أنّ الكائن obj
تغير باستخدام الإسناد.
ملاحظة: إذا كان foo
متغيرًا من المستوى الأعلى، فيمكنك بسهولة إخبار إطار Svelte بتحديث الكائن obj
عندما يتغير المتغير foo
باستخدام تعليمة التفاعل التالية: $: foo, obj = obj
، وبالتالي يُعرَّف foo
على أنه اعتمادية، وكلما تغير، سيعمل إطار عمل Svelte على تشغيل عملية الإسناد obj = obj
.
إذا شغلت ما يلي في الدالة checkAllTodos()
:
todos.forEach((t) => t.completed = completed);
لن يلحظ إطار Svelte تغيّر المصفوفة todos
لأنه لا يعرف أننا نعدّلها عند تحديث المتغير t
ضمن التابع forEach()
، ويُعَدّ ذلك منطقيًا، إذ سيعرف إطار Svelte عمل التابع forEach()
الداخلي إذا حدث عكس ذلك، لذا سيُطبَّق الأمر نفسه بالنسبة لأيّ تابع مرتبط بكائن أو مصفوفة، لكن هناك تقنيات مختلفة يمكننا تطبيقها لحل هذه المشكلة، وتتضمن جميعها إسناد قيمة جديدة للمتغير المُراقَب.
يمكننا إخبار إطار عمل Svelte بتحديث المتغير باستخدام إسناد ذاتي كما يلي:
const checkAllTodos = (completed) => { todos.forEach((t) => t.completed = completed); todos = todos; }
تحل هذه الطريقة المشكلة، إذ سيرفع إطار Svelte رايةً تعبِّر عن تغيير المصفوفة todos
ويزيل الإسناد الذاتي الذي يراه زائدًا، كما يمكن أن تبدو هذه الطريقة غريبةً، ولكنها تُعَدّ جيدةً ومختصَرةً.
يمكننا الوصول أيضًا إلى المصفوفة todos
باستخدام الفهرس كما يلي:
const checkAllTodos = (completed) => { todos.forEach((t, i) => todos[i].completed = completed); }
تعمل الإسنادات إلى خاصيات المصفوفات والكائنات مثل obj.foo += 1
أو array[i] = x
بالطريقة نفسها للإسنادات إلى القيم نفسها، فإذا حلّل إطار عمل Svelte هذه الشيفرة، فيمكنه اكتشاف أنّ المصفوفة todos
تُعدَّل.
يوجد حل آخر هو إسناد مصفوفة جديدة إلى المصفوفة todos
، إذ تحتوي هذه المصفوفة الجديدة على نسخة من جميع المهام مع تحديث الخاصية completed
وفقًا لذلك كما يلي:
const checkAllTodos = (completed) => { todos = todos.map((t) => ({ ...t, completed })); }
نستخدِم في هذه الحالة التابع map()
الذي يعيد مصفوفةً جديدةً مع نتائج تنفيذ الدالة المتوفرة لكل عنصر، إذ تعيد الدالة نسخةً من كل مهمة باستخدام صيغة الانتشار Spread Syntax وتعيد كتابة خاصية القيمة completed
وفقًا لذلك، وتتمثل فائدة هذا الحل في إعادة مصفوفة جديدة مع كائنات جديدة وتجنب تغيير المصفوفة todos
الأصلية.
ملاحظة: يتيح إطار Svelte تحديد خيارات مختلفة تؤثر على كيفية عمل المصرِّف Compiler، إذ يخبر الخيار <svelte:options immutable={true}/>
المصرِّف بأنك تتعهد بعدم تغيير أيّ كائنات، مما يتيح له بأن يكون أقل تحفظًا بشأن التحقق من تغيير القيم وإنشاء شيفرة أبسط وأكثر فعالية.
تتضمن كل هذه الحلول إسنادًا يكون فيه المتغير المحدَّث في الجانب الأيسر من المساواة، وستسمح جميعها لإطار Svelte بملاحظة تعديل المصفوفة todos
، لذا اختر أحد هذه الحلول وحدّث الدالة checkAllTodos()
كما هو مطلوب، ويجب الآن أن تكون قادرًا على تحديد جميع مهامك وإلغاء تحديدها دفعةً واحدةً.
الانتهاء من المكون MoreActions
سنضيف أحد تفاصيل إمكانية الاستخدام إلى مكوننا، إذ سنعطّل الأزرار في حالة عدم وجود مهام لمعالجتها من خلال استخدام المصفوفة todos
بوصفها خاصيةً وضبط الخاصية disabled
لكل زر وفقًا لذلك.
أولًا، عدّل المكوِّن MoreActions.svelte
كما يلي:
<script> import { createEventDispatcher } from 'svelte' const dispatch = createEventDispatcher() export let todos let completed = true const checkAll = () => { dispatch('checkAll', completed) completed = !completed } const removeCompleted = () => dispatch('removeCompleted') $: completedTodos = todos.filter(t => t.completed).length </script> <div class="btn-group"> <button type="button" class="btn btn__primary" disabled={todos.length === 0} on:click={checkAll}>{completed ? 'Check' : 'Uncheck'} all</button> <button type="button" class="btn btn__primary" disabled={completedTodos === 0} on:click={removeCompleted}>Remove completed</button> </div>
صرّحنا عن متغير التفاعل completedTodos
لتفعيل أو تعطيل زر "إزالة المهام المكتملة Remove Completed".
لا تنسى تمرير الخاصية إلى المكوِّن MoreActions
من المكوِّن Todos.svelte
حيث يُستدعَى المكوِّن كما يلي:
<MoreActions {todos} on:checkAll={(e) => checkAllTodos(e.detail)} on:removeCompleted={removeCompletedTodos} />
التعامل مع نموذج DOM: التركيز على التفاصيل
أكملنا جميع الوظائف المطلوبة للتطبيق، وسنركِّز على بعض ميزات سهولة الوصول Accessibility التي ستحسّن إمكانية استخدام تطبيقنا لكل من مستخدمِي لوحة المفاتيح فقط وقارئات الشاشة، كما يواجه تطبيقنا حاليًا مشكلتين متعلقتين بسهولة وصول استخدام لوحة المفاتيح وتتضمن إدارة التركيز، لذا لنلقِ نظرةً على هذه المشاكل.
استكشاف مشاكل سهولة الوصول لمستخدمي لوحة المفاتيح في تطبيقنا
سيكتشف مستخدِمو لوحة المفاتيح حاليًا أنّ تدفق التركيز في تطبيقنا لا يمكن التنبؤ به أو غير مترابط، فإذا نقرت على حقل الإدخال في الجزء العلوي من تطبيقنا، فسترى حدًّا سميكًا ومتقطعًا حول هذا الحقل، إذ يُعَد هذا الحدّ المؤشر المرئي على أنّ المتصفح يركِّز حاليًا على هذا العنصر.
إذا كنت من مستخدمِي الفأرة، فيمكن أن تتخطى هذه الإشارة المرئية، ولكن إذا أردت العمل باستخدام لوحة المفاتيح فقط، فمعرفة عنصر التحكم المُركَّز عليه أمرٌ بالغ الأهمية، إذ يخبرنا هذا التركيز أيّ عنصر تحكم سيتلقى ضغطات المفاتيح، فإذا ضغطت على مفتاح Tab
بصورة متكررة، فسترى مؤشر التركيز المتقطع يتنقل بين جميع العناصر القابلة للتركيز على الصفحة، وإذا نقلت التركيز إلى زر "التعديل Edit" وضغطتَ على مفتاح Enter
، فسيختفي التركيز فجأة دون إمكانية تحديد عنصر التحكم الذي سيتلقى ضغطات المفاتيح. إذا ضغطت على مفتاح Escape
أو Enter
، فلن يحدث شيء؛ أما إذا نقرت على زر "الإلغاء Cancel" أو "الحفظ Save"، فسيختفي التركيز مرةً أخرى، كما سيكون هذا السلوك محيرًا بالنسبة لمستخدِم يعمل باستخدام لوحة المفاتيح.
كما نود إضافة بعض ميزات إمكانية الاستخدام مثل تعطيل زر "الحفظ Save" عندما تكون الحقول المطلوبة فارغةً، أو التركيز على بعض عناصر HTML أو التحديد التلقائي للمحتويات عند التركيز على حقل إدخال النص، كما يجب الوصول برمجيًا إلى عقد نموذج DOM لتشغيل دوال مثل الدالتين focus()
و select()
بهدف تطبيق جميع هذه الميزات، ويجب استخدام التابعين addEventListener()
و removeEventListener()
لتشغيل مهام محددة عندما يتلقى عنصر التحكم التركيز.
تكمن المشكلة في أنّ جميع عقد نموذج DOM ينشئها إطار عمل Svelte ديناميكيًا في وقت التشغيل، لذا علينا الانتظار حتى إنشائها وإضافتها إلى نموذج DOM لاستخدامها، إذ يجب التعرف على دورة حياة المكونات لفهم متى يمكننا الوصول إليها.
إنشاء المكون NewTodo
أنشئ ملف مكوِّن جديد وعدّل الشيفرة لإصدار الحدث addTodo
من خلال تمرير اسم المهمة الجديدة مع التفاصيل الإضافية كما يلي:
أولًا، أنشئ ملفًا جديدًا بالاسم components/NewTodo.svelte
.
ضع بعد ذلك المحتويات التالية ضمن هذا الملف:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); let name = ''; const addTodo = () => { dispatch('addTodo', name); name = ''; } const onCancel = () => name = ''; </script> <form on:submit|preventDefault={addTodo} on:keydown={(e) => e.key === 'Escape' && onCancel()}> <h2 class="label-wrapper"> <label for="todo-0" class="label__lg">What needs to be done?</label> </h2> <input bind:value={name} type="text" id="todo-0" autoComplete="off" class="input input__lg" /> <button type="submit" disabled={!name} class="btn btn__primary btn__lg">Add</button> </form>
ربطنا هنا العنصر <input> بالمتغير name
باستخدام bind:value={name}
وعطّلنا زر "الإضافة Add" عندما يكون حقل الإدخال فارغًا -أي لا يحتوي على محتوى نصي- باستخدام disabled={!name}
، كما عالجنا استخدام مفتاح Escape
باستخدام on:keydown={(e) => e.key === 'Escape' && onCancel()}
، إذ نشغّل التابع onCancel()
الذي يمسح المتغير name
في كل مرة نضغط فيها على مفتاح Escape
.
يجب الآن استيراد import
المكوِّن NewTodo
واستخدامه ضمن المكوِّن Todos
وتحديث الدالة addTodo()
للحصول على اسم المهمة الجديد، لذا أضف تعليمة الاستيراد التالية بعد تعليمات الاستيراد الأخرى الموجودة ضمن Todos.svelte:
import NewTodo from './NewTodo.svelte'
عدّل الدالة addTodo()
بعد ذلك كما يلي:
function addTodo(name) { todos = [...todos, { id: newTodoId, name, completed: false }] }
تتلقى الدالة addTodo()
الآن اسم المهمة الجديدة مباشرةً، لذلك لم نَعُد بحاجة المتغير newTodoName
لإعطائه قيمة، إذ سيهتم المكوِّن NewTodo
بذلك.
ملاحظة: تُعَدّ الصيغة { name }
اختصارًا للصيغة { name: name }
، إذ يأتي هذا الاختصار من لغة جافاسكربت وليس له علاقة بإطار Svelte مع توفير بعض الإلهام للاختصارات الخاصة بإطار Svelte.
أخيرًا، استبدل شيفرة HTML الخاصة بنموذج NewTodo باستدعاء المكوِّن NewTodo
كما يلي:
<!-- NewTodo --> <NewTodo on:addTodo={(e) => addTodo(e.detail)} />
التعامل مع عقد نموذج DOM باستخدام الموجه bind:this={dom_node}
نريد الآن أن يعود التركيز إلى العنصر <input>
الخاص بالمكون NewTodo
في كل مرة يُضغَط فيها على زر "الإضافة Add"، لذا سنحتاج مرجعًا إلى عقدة نموذج DOM الخاصة بحقل الإدخال، إذ يوفر إطار عمل Svelte طريقةً لذلك باستخدام الموجِّه bind:this={dom_node}
، كما يسند إطار Svelte مرجع عقدة DOM إلى متغير محدد بمجرد تثبيت المكوِّن وإنشاء عقدة DOM.
لننشئ المتغير nameEl
ونربطه بحقل الإدخال باستخدام bind:this={nameEl}
، ثم سنستدعي التابع nameEl.focus()
ضمن الدالة addTodo()
لإعادة التركيز إلى العنصر <input>
مرةً أخرى بعد إضافة المهام الجديدة، وسنطبّق الشيء نفسه عندما يضغط المستخدِم على مفتاح Escape
باستخدام الدالة onCancel()
.
عدّل محتويات المكوِّن NewTodo.svelte
كما يلي:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); let name = ''; let nameEl; // مرجع إلى عقدة حقل الإدخال name في نموذج DOM const addTodo = () => { dispatch('addTodo', name); name = ''; nameEl.focus(); // ركّز على حقل الإدخال name } const onCancel = () => { name = ''; nameEl.focus(); // ركّز على حقل الإدخال name } </script> <form on:submit|preventDefault={addTodo} on:keydown={(e) => e.key === 'Escape' && onCancel()}> <h2 class="label-wrapper"> <label for="todo-0" class="label__lg">What needs to be done?</label> </h2> <input bind:value={name} bind:this={nameEl} type="text" id="todo-0" autoComplete="off" class="input input__lg" /> <button type="submit" disabled={!name} class="btn btn__primary btn__lg">Add</button> </form>
جرِّب التطبيق واكتب اسم مهمة جديدة في حقل الإدخال <input>
واضغط على المفتاح tab
للتركيز على زر "الإضافة Add"، ثم اضغط على مفتاح Enter
أو Escape
لترى كيف يستعيد حقل الإدخال التركيز.
التركيز التلقائي على حقل الإدخال
الميزة التالية التي سنضيفها إلى المكوِّن NewTodo
هي الخاصية autofocus
التي ستسمح بتحديد أننا نريد التركيز على حقل الإدخال <input>
في صفحة التحميل.
محاولتنا الأولى هي كما يلي: لنحاول إضافة الخاصية autofocus
واستدعاء التابع nameEl.focus()
في كتلة القسم <script>
، لذا عدِّل الجزء الأول من القسم <script>
الخاص بالمكوِّن NewTodo.svelte
(الأسطر الأربعة الأولى) لتبدو كما يلي:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let autofocus = false; let name = ''; let nameEl; // مرجع إلى عقدة حقل الإدخال name في نموذج DOM if (autofocus) nameEl.focus();
عُد الآن إلى المكوِّن Todos
ومرّر الخاصية autofocus
إلى استدعاء المكوِّن <NewTodo>
كما يلي:
<!-- NewTodo --> <NewTodo autofocus on:addTodo={(e) => addTodo(e.detail)} />
إذا جربت تطبيقك، فسترى أنّ الصفحة فارغة حاليًا، وسترى في طرفية أدوات تطوير الويب خطأً بالشكل: TypeError: nameEl is undefined
.
دورة حياة المكون والدالة onMount()
يشغّل إطار Svelte شيفرة التهيئة -أي قسم <script>
الخاص بالمكون- عند إنشاء نسخة من هذا المكون، ولكن تكون جميع العقد التي يتألف منها المكوِّن غير مرتبطة بنموذج DOM في تلك اللحظة، وهي في الحقيقة غير موجودة أصلًا، كما يمكن أن تتساءل عن كيفية معرفة وقت إنشاء المكوِّن فعليًا وتثبيته على نموذج DOM، والإجابة هي أنه لكل مكوِّن دورة حياة تبدأ عند إنشائه وتنتهي عند تدميره، وهناك عدد من الدوال التي تسمح بتشغيل الشيفرة في اللحظات المهمة خلال دورة الحياة هذه.
الدالة التي ستستخدِمها بكثرة هي الدالة onMount()
والتي تتيح تشغيل دالة رد نداء Callback بمجرد تثبيت المكوِّن على نموذج DOM، لذا لنجربها ونرى ما سيحدث للمتغير nameEl
.
أضِف أولًا السطر التالي في بداية القسم <script>
الخاص بالمكوِّن NewTodo.svelte
:
import { onMount } from 'svelte';
وأضِف الأسطر التالية في نهايته:
console.log('initializing:', nameEl); onMount( () => { console.log('mounted:', nameEl); })
احذف الآن السطر if (autofocus) nameEl.focus()
لتجنب الخطأ الذي رأيناه سابقًا.
سيعمل التطبيق الآن مرةً أخرى، وسترى ما يلي في الطرفية:
initializing: undefined mounted: <input id="todo-0" class="input input__lg" type="text" autocomplete="off">
يكون المتغير nameEl
غير مُعرَّف أثناء تهيئة المكوِّن، وهو أمر منطقي لأن عقدة حقل الإدخال <input>
غير موجودة حتى الآن، لذا أسند إطار عمل Svelte مرجع العقدة <input>
في نموذج DOM إلى المتغير nameEl
بفضل الموجّه bind:this={nameEl}
بعد تثبيت المكوِّن.
يمكنك تشغيل وظيفة التركيز التلقائي من خلال استبدال كتلة onMount()
ضمن console.log()
السابقة بما يلي:
onMount(() => autofocus && nameEl.focus()); // سنشغّل التابع nameEl.focus() إذا كانت قيمة autofocus هي true
انتقل إلى تطبيقك مرةً أخرى وسترى الآن تركيز حقل الإدخال <input>
على صفحة التحميل.
انتظار تحديث نموذج DOM باستخدام الدالة tick()
سنهتم الآن بتفاصيل إدارة تركيز المكون Todo
، إذ نريد أولًا أن يستلم تعديل حقل الإدخال <input>
الخاص بالمكوِّن Todo
التركيز عند الدخول في وضع التعديل من خلال الضغط على زر "التعديل Edit"، كما سننشئ المتغير nameEl
ضمن المكوِّن Todo.svelte
وسنستدعي التابع nameEl.focus()
بعد ضبط المتغير editing
على القيمة true
.
أولًا، افتح الملف components/Todo.svelte
وأضِف التصريح عن المتغير nameEl
التالي بعد التصريح عن المتغيرين editing
و name
مباشرةً:
let nameEl; // مرجع إلى عقدة حقل الإدخال name في نموذج DOM
ثانيًا، عدّل الدالة onEdit()
كما يلي:
function onEdit() { editing = true; // الدخول في وضع التعديل nameEl.focus(); // ضبط التركيز على حقل الإدخال name }
أخيرًا، اربط المتغير nameEl
بحقل الإدخال <input>
من خلال تعديله كما يلي:
<input bind:value={name} bind:this={nameEl} type="text" id="todo-{todo.id}" autocomplete="off" class="todo-text" />
ولكن ستحصل على خطأ بالشكل: "TypeError: nameEl is undefined" في الطرفية عند الضغط على زر تعديل المهمة.
لا يحدّث إطار عمل Svelte نموذج DOM مباشرةً عند تحديث حالة المكوِّن، وإنما ينتظر حتى المهمة السريعة microtask التالية لمعرفة ما إذا كانت هناك أيّ تغييرات أخرى يجب تطبيقها بما في ذلك التغييرات في المكونات الأخرى، مما يؤدي إلى تجنب العمل غير الضروري ويسمح للمتصفح بتجميع الأشياء بطريقة أكثر فعالية.
لا يكون تعديل حقل الإدخال <input>
مرئيًا في هذه الحالة لأنه غير موجود في نموذج DOM عندما يكون للمتغير editing
القيمة false
، لذا اضبط editing = true
في الدالة onEdit()
وحاول بعد ذلك مباشرةً الوصول إلى المتغير nameEl
ونفّذ التابع nameEl.focus()
، ولكن المشكلة هنا هي أنّ إطار عمل Svelte لم يحدّث نموذج DOM بعد.
تتمثل إحدى طرق حل هذه المشكلة في استخدام التابع setTimeout()
لتأخير استدعاء التابع nameEl.focus()
حتى دورة الأحداث التالية وإعطاء إطار عمل Svelte الفرصة لتحديث نموذج DOM كما يلي:
function onEdit() { editing = true; // الدخول في وضع التعديل setTimeout(() => nameEl.focus(), 0); // استدعاء غير متزامن لضبط التركيز على حقل الإدخال name }
الحل السابق جيد، ولكنه غير مرتب إلى حد ما، إذ يوفر إطار Svelte طريقةً أفضل للتعامل مع هذه الحالات، حيث تعيد الدالة tick()
وعدًا Promise يُحَل بمجرد تطبيق أيّ تغييرات على حالة مُعلَّقة في نموذج DOM، أو مباشرةً إذا لم تكن هناك تغييرات على حالة مُعلَّقة.
استورد أولًا tick
في بداية القسم <script>
مع تعليمات الاستيراد الموجودة مسبقًا كما يلي:
import { tick } from 'svelte'
استدعِ بعد ذلك الدالة tick()
مع المعامِل await
من دالة غير متزامنة، وعدّل الدالة onEdit()
كما يلي:
async function onEdit() { editing = true; // الدخول في وضع التعديل await tick(); nameEl.focus(); }
إذا جربت التطبيق الآن، فسترى أنّ كل شيء يعمل كما هو متوقع.
إضافة وظائف إلى عناصر HTML باستخدام الموجه use:action
نريد بعد ذلك أن يحدّد حقل الإدخال <input>
كل النص عند التركيز عليه، كما نريد تطوير ذلك بطريقة يمكن إعادة استخدامه بسهولة على أيّ عنصر <input>
في HTML وتطبيقه بطريقة تصريحية، وسنستخدِم هذا المتطلب بوصفه سببًا لإظهار ميزة قوية جدًا يوفرها إطار Svelte لإضافة وظائف لعناصر HTML العادية، وهذه الميزة هي الإجراءات actions.
يمكنك تحديد نص عقدة حقل إدخال في نموذج DOM من خلال استدعاء التابع select()
، إذ يجب استخدام مستمع أحداث لاستدعاء هذه الدالة كلما انتقل التركيز إلى هذه العقدة كما يلي:
node.addEventListener('focus', event => node.select())
كما يجب استدعاء الدالة removeEventListener()
عند تدمير العقدة لتجنب تسرّب الذاكرة Memory Leak.
ملاحظة: كل ما سبق هو مجرد وظيفة قياسية من واجهة WebAPI دون وجود شيء خاص بإطار عمل Svelte.
يمكن تحقيق كل ذلك في المكوِّن Todo
كلما أضفنا أو أزلنا عنصر <input>
من نموذج DOM، ولكن يجب أن نكون حريصين جدًا على إضافة مستمع الأحداث بعد إضافة العقدة إلى نموذج DOM وإزالة المستمع قبل إزالة العقدة من نموذج DOM. كما أنّ هذا الحل لن يكون قابلًا لإعادة الاستخدام بصورة كبيرة، وهنا يأتي دور إجراءات إطار Svelte التي تسمح بتشغيل دالة كلما أُضيف عنصر إلى نموذج DOM وبعد إزالته من نموذج DOM.
سنعرّف دالة بالاسم selectOnFocus()
تأخذ عقدة على أساس معامل لها، وستضيف هذه الدالة مستمع أحداث إلى تلك العقدة بحيث يُحدَّد النص كلما انتقل التركيز إليها، ثم ستعيد كائنًا مع الخاصية destroy
التي سينفذها إطار Svelte بعد إزالة العقدة من نموذج DOM، وسنزيل هنا المستمع للتأكّد من أننا لا نترك أيّ تسرّب للذاكرة خلفنا.
أولًا، لننشئ الدالة selectOnFocus()
، لذا أضف ما يلي إلى أسفل القسم <script>
الخاص بالمكوِّن Todo.svelte
:
function selectOnFocus(node) { if (node && typeof node.select === 'function' ) { // تأكّد من أن العقدة مُعرَّفة ولديها التابع select() const onFocus = event => node.select(); // معالج الحدث node.addEventListener('focus', onFocus); // استدعِ التابع onFocus() عندما ينتقل التركيز إلى العقدة return { destroy: () => node.removeEventListener('focus', onFocus) // سيُنفَّذ هذا السطر عند إزالة العقدة من نموذج DOM } } }
يجب الآن إعلام حقل الإدخال <input>
بأن يستخدِم هذه الدالة من خلال الموجّه use:action
كما يلي:
<input use:selectOnFocus />
نطلب باستخدام هذا الموجّه من إطار Svelte تشغيل هذه الدالة وتمرير عقدة نموذج DOM الخاصة بحقل الإدخال <input>
بوصفها معاملًا لها بمجرد تثبيت المكوِّن على نموذج DOM، وسيكون مسؤولًا عن تنفيذ الدالة destroy
عند إزالة المكوِّن من نموذج DOM، وبالتالي يهتم Svelte بدورة حياة المكوِّن باستخدام الموجّه use
، وسيكون العنصر <input>
في حالتنا كما يلي:
عدّل أول زوج تسمية أو عنوان/حقل إدخال label/input للمكوِّن ضمن قالب التعديل على النحو التالي:
<label for="todo-{todo.id}" class="todo-label">New name for '{todo.name}'</label> <input bind:value={name} bind:this={nameEl} use:selectOnFocus type="text" id="todo-{todo.id}" autocomplete="off" class="todo-text" />
انتقل إلى تطبيقك واضغط على زر تعديل المهام ثم اضغط على المفتاح Tab
لإبعاد التركيز عن العنصر <input>
، ثم انقر عليه وسترى تحديد نص حقل الإدخال بالكامل.
جعل الإجراء قابلا لإعادة الاستخدام
لنجعل الآن هذه الدالة قابلة لإعادة الاستخدام بين المكونات، إذ تُعَدّ الدالة selectOnFocus()
مجرد دالة لا تعتمد على المكوِّن Todo.svelte
، لذا يمكننا وضعها في ملف واستخدامها من هناك.
أولًا، أنشئ ملفًا جديدًا بالاسم actions.js ضمن المجلد src.
ثانيًا، ضع فيه المحتوى التالي:
export function selectOnFocus(node) { if (node && typeof node.select === 'function' ) { // تأكّد من أن العقدة مُعرَّفة ولديها التابع select() const onFocus = event => node.select(); // معالج الحدث node.addEventListener('focus', onFocus); // يُستدعى عند التركيز على القعدة return { destroy: () => node.removeEventListener('focus', onFocus) // سيُنفَّذ هذا السطر عند إزالة العقدة من نموذج DOM } } }
استورده من داخل المكوِّن Todo.svelte
من خلال إضافة تعليمة الاستيراد التالية:
import { selectOnFocus } from '../actions.js'
احذف تعريف الدالة selectOnFocus()
من المكوِّن Todo.svelte
، لأننا لم نعُد بحاجة إليها هناك.
إعادة استخدام الإجراء
لنستخدم الإجراء في المكوِّن NewTodo.svelte
لإثبات إمكانية إعادة استخدامه.
أولًا، استورد الدالة selectOnFocus()
من الملف actions.js في الملف NewTodo.svelte كما يلي:
import { selectOnFocus } from '../actions.js';
ثانيًا، أضف الموجّه use:selectOnFocus
إلى العنصر <input>
كما يلي:
<input bind:value={name} bind:this={nameEl} use:selectOnFocus type="text" id="todo-0" autocomplete="off" class="input input__lg" />
يمكننا إضافة وظائف لعناصر HTML العادية بطريقة قابلة لإعادة الاستخدام وتصريحية باستخدام بضعة أسطر من الشيفرة البرمجية، إذ يتطلب الأمر استيرادًا import
وموجّهًا قصيرًا مثل الموجّه use:selectOnFocus
الذي يوضِّح الغرض منه، ويمكننا تحقيق ذلك دون الحاجة إلى إنشاء عنصر مُغلِّف مخصَّص مثل TextInput
أو MyInput
أو ما شابه ذلك، كما يمكنك إضافة العديد من موجّهات use:action
إلى عنصر ما.
كما أنه ليس علينا أن نعاني باستخدام الدوال onMount()
أو onDestroy()
أو tick()
، إذ يهتم الموجّه use
بدورة حياة المكوِّن.
تحسينات الإجراءات الأخرى
كان علينا في القسم السابق أثناء العمل مع مكونات Todo
التعاملَ مع الدوال bind:this
و tick()
و async
للتركيز على حقل الإدخال <input>
بمجرد إضافته إلى نموذج DOM.
يمكننا تطبيق ذلك باستخدام الإجراءات كما يلي:
const focusOnInit = (node) => node && typeof node.focus === 'function' && node.focus();
يجب بعد ذلك إضافة موجّه use:
آخر في شيفرة HTML كما يلي:
<input bind:value={name} use:selectOnFocus use:focusOnInit />
يمكن الآن أن تكون الدالة onEdit()
أبسط كما يلي:
function onEdit() { editing = true; // الدخول في وضع التعديل }
لنعُد إلى المكوِّن Todo.svelte
ونركّز على زر "التعديل Edit" بعد أن يضغط المستخدِم على زر "الحفظ Save" أو "الإلغاء Cancel".
يمكننا محاولة إعادة استخدام الإجراء focusOnInit
مرة أخرى من خلال إضافة الموجّه use:focusOnInit
إلى زر "التعديل Edit"، لكننا سندخِل بذلك زلة برمجية، إذ سينتقل التركيز عند إضافة مهمة جديدة إلى زر "التعديل Edit" الخاص بالمهمة التي أُضيفت مؤخرًا بسبب تشغيل الإجراء focusOnInit
عند إنشاء المكوِّن.
لا نريد ذلك، وإنما نريد أن يستلم زر "التعديل Edit" التركيز فقط عندما يضغط المستخدِم على زر "الحفظ Save" أو "الإلغاء Cancel".
لذا ارجع إلى الملف Todo.svelte، إذ سننشئ أولًا رايةً بالاسم editButtonPressed
ونهيّئها بالقيمة false
، لذا أضف ما يلي بعد تعريفات المتغيرات الأخرى:
let editButtonPressed = false; // تتبّع إذا ضُغِط على زر التعديل للتركيز عليه بعد الإلغاء أو الحفظ
سنعدّل بعد ذلك وظيفة زر "التعديل Edit" لحفظ هذه الراية وإنشاء إجرائها الخاص، لذا عدّل الدالة onEdit()
كما يلي:
function onEdit() { editButtonPressed = true; // سيؤدي ضغط المستخدم على زر التعديل إلى عودة التركيز إليه editing = true; // الدخول في وضع التعديل }
أضِف بعد ذلك تعريف الدالة focusEditButton()
التالي:
const focusEditButton = (node) => editButtonPressed && node.focus();
أخيرًا، استخدم الموجّه use:focusEditButton
مع زر "التعديل Edit" كما يلي:
<button type="button" class="btn" on:click={onEdit} use:focusEditButton> Edit<span class="visually-hidden"> {todo.name}</span> </button>
جرّب تطبيقك مرةً أخرى، إذ يُنفَّذ الإجراء focusEditButton
في هذه المرحلة في كل مرة يُضاف فيها زر "التعديل Edit" إلى نموذج DOM، ولكنه سيعطي التركيز فقط للزر إذا كانت قيمة الراية editButtonPressed
هي true
.
ملاحظة: لم نتعمق كثيرًا في الإجراءات هنا، إذ يمكن أن تحتوي الإجراءات على معامِلات تفاعلية، ويتيح إطار Svelte اكتشاف متى يتغير أيّ من هذه المعامِلات لنتمكن من إضافة وظائف تتكامل جيدًا مع نظام التفاعل في إطار Svelte، كما تُعَدّ الإجراءات مفيدةً للتكامل بسلاسة مع المكتبات الخارجية.
ربط المكونات: الوصول إلى توابع ومتغيرات المكون باستخدام الموجه bind:this={component}
توجد مشكلة أخرى وهي أنه يتلاشى التركيز عندما يضغط المستخدِم على زر "الحذف Delete"، إذ تتضمن الميزة الأخيرة التي سنشرحها في هذا المقال ضبط التركيز على عنوان الحالة بعد حذف مهمة.
اخترنا التركيز على عنوان الحالة بسبب حذف العنصر الذي جرى التركيز عليه، لذلك لا يوجد عنصر آخر واضح لتلقي التركيز، إذ يُعَدّ عنوان الحالة قريبًا من قائمة المهام، وهو طريقة مرئية لمعرفة إزالة المهمة بالإضافة إلى توضيح ما حدث لمستخدِمي قارئ الشاشة.
أولًا، أنشئ ملفًا جديدًا بالاسم components/TodosStatus.svelte
.
ثانيًا، أضِف إليه المحتويات التالية:
<script> export let todos; $: totalTodos = todos.length; $: completedTodos = todos.filter((todo) => todo.completed).length; </script> <h2 id="list-heading"> {completedTodos} out of {totalTodos} items completed </h2>
ثالثًا، استورد هذا الملف في بداية المكوِّن Todos.svelte
من خلال إضافة تعليمة الاستيراد import
التالية بعد تعليمات الاستيراد الأخرى:
import TodosStatus from './TodosStatus.svelte';
رابعًا، استبدل عنوان الحالة <h2>
ضمن الملف Todos.svelte باستدعاء المكوِّن TodosStatus
من خلال تمرير todos
إليه بوصفها خاصيةً كما يلي:
<TodosStatus {todos} />
خامسًا، أزِل المتغيرين totalTodos
و completedTodos
من المكوِّن Todos.svelte
، إذ ما عليك سوى إزالة السطرين $: totalTodos = ...
و$: completedTodos = ...
وإزالة المرجع إلى المتغير totalTodos
عندما نحسب newTodoId
واستخدم todos.length
بدلًا من ذلك، أي استبدل الكتلة التي تبدأ بالسطر let newTodoId
بما يلي:
$: newTodoId = todos.length ? Math.max(...todos.map(t => t.id)) + 1 : 1;
يعمل كل شيء كما هو متوقع، واستخرجنا للتو آخر جزء من شيفرة HTML إلى مكوِّنه الخاص.
يجب الآن إيجاد طريقة للتركيز على تسمية الحالة <h2>
بعد إزالة المهمة، إذ رأينا حتى الآن كيفية إرسال المعلومات إلى مكوِّن باستخدام الخاصيات Props، وكيف يمكن للمكوِّن التواصل مع المكوِّن الأب عن طريق إصدار أحداث أو استخدام ربط البيانات ثنائي الاتجاه، إذ يمكن للمكوِّن الابن الحصول على مرجع إلى العقدة <h2>
باستخدام الموجّه bind:this={dom_node}
ويمكن للمكونات الخارجية الوصول إليه باستخدام ربط البيانات ثنائي الاتجاه، لكن سيؤدي ذلك إلى كسر تغليف المكوِّن، لذلك نحن بحاجة إلى المكوِّن TodosStatus
للوصول إلى تابع يمكن للمكوِّن الابن استدعاؤه للتركيز علي، إذ تُعَدّ حاجة المكوِّن لإمكانية وصول المستخدِم لبعض السلوك أو المعلومات أمرًا شائعًا جدًا، لذا لنرى كيفية تحقيق ذلك في إطار عمل Svelte.
رأينا سابقًا أن إطار عمل Svelte يستخدِم التعليمة export let varname = ...
للتصريح عن الخاصيات، ولكن إذا صدّرتَ ثابتًا const
أو صنفًا class
أودالةً function
بدلًا من استخدام let
لوحدها، فستكون للقراءة فقط خارج المكوِّن، وتُعَدّ تعابير الدوال خاصيات صالحةً.
تُعَدّ التصريحات الثلاثة الأولى في المثال التالي خاصيات، والتصريحات الأخرى هي عبارة عن قيم مُصدَّرة:
<script> export let bar = "optional default initial value"; // خاصية export let baz = undefined; // خاصية export let format = n => n.toFixed(2); // خاصية // these are readonly export const thisIs = "readonly"; // تصدير للقراءة فقط export function greet(name) { // تصدير للقراءة فقط alert(`hello ${name}!`); } export const greet = (name) => alert(`hello ${name}!`); // تصدير للقراءة فقط </script>
لننشئ تابعًا بالاسم focus()
يركّز على العنوان <h2>
، لذا سنحتاج إلى المتغير headingEl
للاحتفاظ بالمرجع إلى عقدة DOM، ويجب ربطه بالعنصر <h2>
باستخدام الموجّه bind:this={headingEl}
، إذ سيشغّل تابع التركيز فقط headingEl.focus()
.
أولًا، عدّل محتويات المكوِّن TodosStatus.svelte
كما يلي:
<script> export let todos; $: totalTodos = todos.length; $: completedTodos = todos.filter((todo) => todo.completed).length; let headingEl; export function focus() { // shorter version: export const focus = () => headingEl.focus() headingEl.focus(); } </script> <h2 id="list-heading" bind:this={headingEl} tabindex="-1"> {completedTodos} out of {totalTodos} items completed </h2>
لاحظ أننا أضفنا السمة tabindex
إلى العنوان <h2>
للسماح للعنصر بتلقي التركيز برمجيًا، إذ يعطينا استخدام الموجِّه bind:this={headingEl}
مرجعًا إلى عقدة DOM في المتغير headingEl
كما رأينا سابقًا، كما نستخدم بعد ذلك التعليمة export function focus()
لإمكانية الوصول إلى دالة تركّز على العنوان <h2>
، كما يمكنك ربط نسخ المكوِّن باستخدام الموجِّه bind:this={component}
مثل ربط عناصر DOM باستخدام الموجّه bind:this={dom_node}
، لذا تحصل على مرجع لعقدة DOM عند استخدام الموجِّه bind:this
مع عنصر HTML، وتحصل على مرجع إلى نسخة من هذا المكوِّن عندما تفعل ذلك مع مكوِّن Svelte.
ثانيًا، سننشئ أولًا المتغير todosStatus
في Todos.svelte
للربط بنسخة من المكوِّن Todos.svelte
، لذا أضف السطر التالي بعد تعليمات الاستيراد الموجودة مسبقًا:
let todosStatus; // مرجع إلى نسخة من المكون TodosStatus
ثالثًا، أضِف بعد ذلك الموجّه bind:this={todosStatus}
إلى الاستدعاء كما يلي:
<!-- TodosStatus --> <TodosStatus bind:this={todosStatus} {todos} />
رابعًا، يمكننا الآن استدعاء التابع focus()
المُصدَّر من التابع removeTodo()
كما يلي:
function removeTodo(todo) { todos = todos.filter((t) => t.id !== todo.id); todosStatus.focus(); // ركّز على عنوان الحالة }
خامسًا، ارجع إلى تطبيقك، فإذا حذفت أيّ مهمة الآن، فسينتقل التركيز إلى عنوان الحالة، وهذا مفيد لتسليط الضوء على التغيير في عدد المهام لكل من المستخدِمين المبصرين ومستخدِمي قارئات الشاشة.
ملاحظة: يمكن أن تتساءل عن سبب حاجتنا للتصريح عن متغير جديد لربط المكون بالرغم من أنه يمكننا فقط استدعاء التابع TodosStatus.focus()
، إذ يمكن أن يكون لديك العديد من نسخ المكوِّن TodosStatus
النشطة، لذلك تحتاج لطريقة للرجوع إلى كل نسخة معينة، وبالتالي يجب تحديد متغير لربط كل نسخة محددة به.
يمكنك الوصول إلى نسختك من مستودعنا على النحو التالي لمعرفة حالة الشيفرة كما يجب أن تكون في نهاية هذا المقال:
cd mdn-svelte-tutorial/06-stores
أو يمكنك تنزيل محتوى المجلد مباشرةً باستخدام الأمر التالي:
npx degit opensas/mdn-svelte-tutorial/06-stores
تذكَّر تشغيل الأمر npm install && npm run dev
لبدء تشغيل تطبيقك في وضع التطوير، فإذا أردت متابعتنا، فابدأ بكتابة الشيفرة باستخدام الأداة REPL من موقع svelte.dev.
الخلاصة
انتهينا في هذا المقال من إضافة جميع الوظائف المطلوبة إلى تطبيقنا، بالإضافة إلى اهتمامنا بعدد من مشاكل سهولة الوصول وسهولة الاستخدام، وانتهينا من تقسيم تطبيقنا إلى مكونات يمكن إدارتها مع إعطاء كل منها مسؤولية فريدة، كما رأينا بعض تقنيات إطار عمل Svelte المتقدمة مثل:
- التعامل مع اكتشاف التفاعل عند تحديث العناصر والمصفوفات.
-
العمل مع عقد DOM باستخدام الموجّه
bind:this={dom_node}
(ربط عناصر DOM). -
استخدام الدالة
onMount()
الخاصة بدورة حياة المكوِّن. -
إجبار إطار عمل Svelte على حل تغييرات الحالة المُعلَّقة باستخدام الدالة
tick()
. -
إضافة وظائف لعناصر HTML بطريقة تصريحية وقابلة لإعادة الاستخدام باستخدام الموجّه
use:action
. -
الوصول إلى توابع المكونات باستخدام الموجّه
bind:this={component}
(ربط المكونات).
سنرى في المقال التالي كيفية استخدام المخازن Stores للتواصل بين المكونات وإضافة الحركة إلى المكونات.
ترجمة -وبتصرُّف- للمقال Advanced Svelte: Reactivity, lifecycle, accessibility.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.