دورة TanStack Router الكاملة › دورة TanStack Router الكاملة 1 / 1 السابق التالي

التخزين المؤقّت (Caching)

الدرس 30 من 34 · دورة TanStack Router الكاملة · 9 دقيقة و36 ثانية

الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.

مستودع الكود لهذا الدرس

الفرع: caching-1

[00:01] كما ذكرنا، فإنّه في TanStack Router لدينا ذاكرة مؤقّتة (cache) مدمجة، ويمكن لهذا التخزين المؤقّت أن يكون كافيًا — كما هو مذكور هنا — للتطبيقات الصغيرة إلى المتوسطة الحجم.

[00:11] ومع ذلك، يمكنك أيضًا دمج شيءٍ مثل TanStack Query إن كنت تحتاج إلى حلّ تخزينٍ مؤقّتٍ أكثر تقدّمًا. فإذا كنت تعمل على مشروع، فتأكّد أن تلقي نظرة — ربّما — على هذه الصفحة. فهنا لدينا

[00:24] المزايا والعيوب (pros and cons) المسردة للتخزين المؤقّت الذي يأتي مع TanStack Router افتراضيًا. وبقراءة هذا، يمكنك على الأرجح أن تقرّر إن كان بوسعك استخدام هذا التخزين المؤقّت أو أنّك تحتاج إلى استخدام شيءٍ مثل TanStack Query مع TanStack

[00:36] Router. ومع ذلك، ما سنفعله أنّنا سنناقش الذاكرة المؤقّتة المدمجة في TanStack Router. ولن نستخدم TanStack Query في هذا الفيديو. فلنلقي نظرة إذًا على هذا التخزين المؤقّت الافتراضي. فلنعُد إلى

[00:48] تطبيقنا، ولنقرّر — ولنثبّت هنا في مسار التدوينات (posts route) هذا — ولنقل إنّ التدوينة ستستغرق بعض الوقت كي تتحمّل، مثلًا. فيمكننا الانتقال إلى مسار posts/$postId لدينا، ويمكننا جعله يتحمّل أبطأ قليلًا

[01:04] ربّما، عبر دالة التحميل (loader function) لدينا هنا.

[01:06] فلنضيف بعض المهلة الاصطناعية هنا هكذا. فالآن فلنجدّد (refresh) الصفحة — ربّما. وإذا زرنا أيّ تدوينة، فسيستغرق تحميلها بعض الوقت.

[01:20] ومع ذلك، بعد التحميل ستُخزَّن في الذاكرة المؤقّتة. فإذا رجعنا وزارنا التدوينة نفسها مرةً أخرى، فكما ترى لم تستغرق أيّ وقت كي تتحمّل. إذًا، في TanStack Router، لدينا هذا التخزين المؤقّت بنمط stale-while-revalidate (قدّم المخزَّن ثم أعد التحقّق) افتراضيًا،

[01:34] أي أنّه إذا كان لَدَينا شيءٌ في الذاكرة المؤقّتة فسنقدّمه، ثم نحدّث بياناتنا في الخلفية. فإذا تطلّعنا إلى الشبكة (Network) لدينا هنا، فلنمحو — فلنرجع وفلنمحو الشبكة لدينا.

[01:47] إذا زرنا هذه التدوينة من جديد، فكما ترى حصلنا على شيءٍ فورًا.

[01:52] ومع ذلك، ما زالت دالة التحميل قد عملت، وبعد 3 ثوانٍ حصلنا على طلباتنا هنا. فحصلنا على شيءٍ من الذاكرة المؤقّتة، مع أنّنا ما نزال نحدّث بياناتنا كلّ مرّة. وهذا ما يُشار إليه بـ stale-while-revalidate أو SWR. غير أنّ

[02:06] هذه الذاكرة المؤقّتة لا تعيش سوى في الذاكرة؛ فبمجرّد أن نحدّث صفحتنا ستكُون قد اختفت. فلنرجع، مثلًا، وفلنجدّد، وإذا نقرنا على التدوينة نفسها مرةً أخرى الآن فسيعود التحميل، ولن يتقدّم من الذاكرة المؤقّتة

[02:20] شيءٌ، لأنّ الذاكرة المؤقّتة اختفت بما أنّنا حدّثنا صفحتنا. وبالطبع لَدَينا بعض الخيارات لنتلاعب بإعدادات التخزين المؤقّت: كم من الوقت يفترض أن تعيش، وما إلى ذلك. فكي نستعرض

[02:33] هذه الخيارات، فلنأخذ مثالًا سريعًا هنا بإنشاء مسارٍ يعرض كلمةً عشوائية. فهنا سأنشئ مسارًا جديدًا. فلنقل، في مساراتنا، أنشئ ملفًا جديدًا. سأسمّيه random-word أيضًا

[02:51] ومع tsx بالطبع. ولن أكتب هذا من الصفر؛ بل سأضع بعض الأشياء هنا فقط.

[02:59] إنه بسيط إلى حدٍّ بعيد. لدينا مسار يمتلك دالة تحميل. فمن داخل دالة التحميل هذه أجعله بطيئًا قليلًا بإضافة مهلةٍ من ثانيةٍ واحدة هنا، بالطبع فقط لكي نستعرض التخزين المؤقّت. ثم وجدتُ هذا الـ API الذي يولّد كلمةً

[03:13] عشوائية. أنا أستخدم هذا الـ API كي أرجُع هذه الكلمة العشوائية من دالة التحميل لدينا.

[03:17] ثم أصل إلى هذه الكلمة العشوائية — عبر بيانات التحميل (loader data) — كي أعرضها في مكوّن المسار (route component) هذا.

[03:25] فبمجرّد حِفظ هذا، إذا ذهبنا إلى مسار random أو random word فينبغي أن نجد — عذرًا؛ الـ API الذي كنت أستخدمه لم يكن يستجيب وقت التسجيل، فاستبدلته بآخر. فإذا كان هذا أيضًا لا

[03:41] يعمل لديك، فستجد غيرَه كذلك. وفي كلّ حال، الآن بعد أن أصبح لَدَينا هذا سنجد كلمةً عشوائية هنا، ومع كلّ تحديث (refresh) ستكون كلمةً جديدة. فما سأفعله الآن أنّني سأنتقل إلى القائمة لدينا.

[03:57] هذه التي فيها home وabout. سأضع رابطًا (link) إلى مسار random word لدينا. فهنا سأذهب إلى المسار الجذر (root route) وسأضيف هذا الرابط بعد about هنا لياخذنا إلى مسار random word مثل

[04:12] ذلك. فإذا ذهبنا — لنقل — إلى مسار about ثم رجعنا إلى random word فسينبغي أن تُقدَّم لنا الكلمة المخزَّنة مؤقتًا، ثم ستُجلب كلمةٌ جديدة في الخلفية. فإذا نقرنا الآن فسنحصل على هذا. وإذا رجعنا إلى

[04:26] about ثم ننتقل إلى random word فسنحصل على الكلمة نفسها، ثم ستُجلب كلمةٌ جديدة في الخلفية، وهذا سبب إضافتي للمهلة هذه كي ترى أنّك ستحصل في البداية على الكلمة المخزَّنة مؤقتًا. فالآن

[04:39] لَدَينا مثالٌ بسيط كي نرى الخيارات التي يمكننا تغييرها في التخزين المؤقّت. ومع ذلك، قبل أن نفعل ذلك، أحتاج أيضًا إلى إضافة شيءٍ آخر. أحتاج إلى إضافة معامِل بحث (search parameter) هنا نعتمد عليه كي نعرض كلمتنا.

[04:51] فلنقل إنّه ستكُون لَدَينا معامِل بحث lang كي نغيّر لغة الكلمة. وهذا سيكون مهمًّا لأنّنا نحتاج — إن كان لَدَينا لغةٌ مختلفة هنا — إلى امتلاك ذاكرة مؤقّتة مختلفة لكلّ لغة حتى

[05:02] لا نحصل من ذاكرتنا المؤقّتة على لغةٍ خاطئة عندما نُقدَّم هنا — عندما نُقدَّم هنا كلمةً مخزَّنة مؤقتًا. فتذكّر أنّنا ناقشنا سبقًا إنّنا حين نعتمد على شيءٍ في معامِل البحث لدينا فإنّنا نفعل ذلك بطريقةٍ

[05:16] معيّنة. لكن دعني أوّلًا — سأفعل ذلك سريعًا — أضيف معامِل البحث الخاص بي.

[05:21] هل تتذكّر كيف نفعله؟ ننشئ مخططًا (schema) باستخدام Zod. فلنستورد z أصلًا هكذا، ومخططي — معامِل البحث الخاص بي، أي lang — سيُسمّى lang، وسيكُون واحدًا من هذه: الإنجليزية،

[05:35] الإسبانية والفرنسية والصينية. وسأضيف أيضًا هذا النوع lang الذي سيستنتج نوع TypeScript لمعامِل lang هذا من مخطط Zod بواسطة z.infer، وبناءً عليه سيمتلك هذا:

[05:49] الإنجليزية — أو الإسبانية والفرنسية والصينية — أو undefined. سنستخدم هذا لاحقًا. ثم تذكّر أنّنا نضيف هنا خيار validate search ببساطة، ونستخدم Zod validator، ونمرّر له مخططنا وهو هذا هكذا.

[06:09] لكن الآن في دالة التحميل لدينا نحتاج إلى استخدام معامِل البحث هذا، وإلى إضافة معامِل لغة هنا إضافةً إلى معامِل words القائم، وهو سيكُون متغيّرًا. فلنغيّر هذا إذًا إلى قالب (template) و

[06:24] نمرّر متغيّرًا هنا. ومع ذلك، تذكّر أنّنا لا نمتلك وصولًا إلى معامِلات البحث من هنا، وقد ذكرنا أنّ سبب ذلك هو التخزين المؤقّت. فإذا أردنا أن نملك تبعياتٍ من معامِلات البحث لدينا، فإنّنا نفعل ذلك بهذه الطريقة. إذًا

[06:39] نضيف loaderDeps (تبعيات التحميل) هنا، وفي loaderDeps يمكننا تمرير دالة، ومن داخل هذه الدالة يمكننا إرجاع كائن (object). ومن داخل هذه الدالة سنمتلك وصولًا إلى معامِلات البحث لدينا ويمكننا إرجاع

[06:54] قيمة lang التي يمكننا الوصول إليها من معامِلات البحث لدينا، أي lang هكذا.

[07:00] نفعل هذا كي يكون TanStack Router على درايةٍ بما نعتمد عليه من معامِل البحث، فيستطيع إنشاء ذاكرة مؤقّتة منفصلة لكلّ معامِل بحثٍ على حِدة، فلا تختلط ببعضها. فالآن في دالة التحميل لدينا

[07:14] سنمتلك وصولًا إلى هذه التبعيات (dependencies)، ويمكننا ببساطة تمريرها هنا في هذه اللغة. أظنّ أنّ عندي علامة يساوي (equals) ناقصةً هنا.

[07:25] إذًا، language ثم يساوي dependencies.lang — إن لم يكُن موجودًا، لأنّني أجعله اختياريًا (optional) هنا. فإن لم يكُن موجودًا فلنجعله يتراجع إلى الإنجليزية. عذرًا، فلنجعله يتراجع إلى الإنجليزية هكذا.

[07:42] فالآن، إذا لم نمرّر شيئًا هنا فسنحصل على كلمةٍ بالإنجليزية. وإذا مرّرنا lang قيمتَها — لنقل — الفرنسية، فسنحصل بالتالي على شيءٍ بالفرنسية.

[08:00] إذا مرّرنا الصينية مثلًا، فسنحصل على شيءٍ بالصينية. والآن، فلنقل إنّه ستكُون لَدَينا مُبدّل لغة (language switcher) في هذه الصفحة، وسأنجزه سريعًا هنا. فربّما قبل عرض كلمتنا، سأضيف div هذا.

[08:16] فلنستورد Link. وقد رأينا شيئًا كهذا من قبل، فلا حاجة إلى كتابته من الصفر. فنحن نمرّ عبر لغاتنا بحلقة. وهنا أستخدم نوع lang الذي عرّفتُه هنا. وأنا أعرض رابطًا فقط

[08:29] يقودنا إلى المسار نفسه. فهنا لَدَينا to بالنقطة (.)، ونحن فقط نحدّث معامِل البحث — وهو lang — كي يصير lang الموجودة في حلقتنا في ذلك الوقت، وهي ستكُون واحدةً من هذه. فبمجرّد حفظ هذا ستكُون لَدَينا هذا مُبدِّل

[08:43] للّغة. والآن بعد أن أصبح معامِل البحث lang تبعيةً لَدَينا، إذا تنقّلنا بين هذه فلن نحصل على أيّ شيءٍ — أو سيكُون لَدَينا ذاكرة مؤقّتة منفصلة لكلّ مسارٍ من هذه المسارات بحسب

[09:02] lang التي نمرّرها هنا. فلنقل — لنذهب، ربّما — إلى الإنجليزية فنحصل على هذه الكلمة. وإذا انتقلنا إلى الإسبانية فسنحصل على كلمةٍ مختلفة. وإذا رجعنا إلى الإنجليزية فسنحصل على الكلمة المخزَّنة التي سبق خزنها

[09:16] بالإنجليزية، ثم سنحصل على واحدةٍ جديدة في الخلفية. والشيء نفسه إذا انتقلنا الآن إلى الإسبانية فسنحصل على الكلمة القديمة التي حصلنا عليها سابقًا، ثم ستُجلب واحدةٌ جديدة في الخلفية. وبذلك ستأخذ ذاكرتنا المؤقّتة

[09:29] معامِل lang هذا بعين الاعتبار، وستنشئ ذاكراتٍ مؤقّتةً منفصلة بحسب معامِل اللغة هذا.