تتالي إبطال ذاكرة التخزين المؤقت. الجزء 1

لعدة سنوات حتى الآن ، توصي كل مقالة تقريبًا عن أساليب التخزين المؤقت المتقدمة باستخدام التقنيات التالية في الإنتاج:

  • إضافة إلى معلومات أسماء الملفات حول إصدار البيانات الموجودة فيها (عادة في شكل تجزئة البيانات الموجودة في الملفات).
  • تعيين رؤوس HTTP Cache-Control: max-age Expires ، والتي تتحكم في وقت التخزين المؤقت للمواد (مما يلغي إعادة التحقق من صحة المواد ذات الصلة للزائرين العائدين إلى المورد).



جميع الأدوات اللازمة لإنشاء مشاريع أعرف أنها تدعم إضافة إلى ملف التجزئة أسماء محتوياتها. يتم ذلك باستخدام قاعدة تكوين بسيطة (مثل ما هو موضح أدناه):

 filename: '[name]-[contenthash].js' 

وقد أدى هذا الدعم الواسع لهذه التكنولوجيا إلى حقيقة أن هذه الممارسة أصبحت شائعة للغاية.

يوصي خبراء أداء مشروع الويب أيضًا باستخدام تقنيات فصل الكود . تسمح هذه التقنيات بتقسيم شفرة JavaScript إلى حزم منفصلة. يمكن تنزيل هذه الحزم عن طريق المتصفح بشكل متوازٍ ، أو حتى عند الضرورة ، بناءً على طلب المستعرض.

واحدة من المزايا العديدة لفصل الكود ، خاصة فيما يتعلق بأفضل تقنيات التخزين المؤقت ، هي أن التغييرات التي تم إجراؤها على ملف منفصل مع الكود المصدري لا تؤدي إلى إبطال ذاكرة التخزين المؤقت للحزمة بأكملها. بمعنى آخر ، إذا تم إصدار تحديث أمني لحزمة npm التي أنشأها مطور "X" ، وتم تجزئة محتويات node_modules بواسطة المطورين ، فلن يتم تغيير سوى الجزء الذي يحتوي على الحزم التي تم إنشاؤها بواسطة "X".

المشكلة هنا هي أنه إذا تم الجمع بين كل هذا ، فإن هذا نادراً ما يؤدي إلى زيادة كفاءة التخزين المؤقت للبيانات على المدى الطويل.

في الممارسة العملية ، تؤدي التغييرات التي يتم إجراؤها على أحد ملفات التعليمات البرمجية المصدر دائمًا إلى إبطال أكثر من ملف إخراج لنظام تجميع الحزمة. وهذا يرجع بالتحديد إلى حقيقة أنه تمت إضافة علامات التجزئة إلى أسماء الملفات التي تعكس إصدارات محتويات هذه الملفات.

اسم ملف الإصدار الإصدار


تخيل أنك قمت بإنشاء ونشر موقع على شبكة الإنترنت. لقد استخدمت تقسيم الشفرة ، ونتيجة لذلك ، يتم تحميل معظم شفرة جافا سكريبت لموقعك عند الطلب.

في مخطط التبعية التالي ، يمكنك رؤية نقطة إدخال التعليمات البرمجية الأساسية - جزء الجذر main ، بالإضافة إلى ثلاثة أجزاء تبعية تم تحميلها بشكل غير متزامن - dep1 و dep2 و dep3 . هناك أيضًا جزء vendor يحتوي على كافة تبعيات الموقع من node_modules . تتضمن جميع أسماء الملفات ، وفقًا لإرشادات التخزين المؤقت ، تجزئات لمحتويات هذه الملفات.


جافا سكريبت نموذجي تبعية شجرة

نظرًا لأن dep2 و dep3 باستيراد الوحدات من جزء vendor ، ثم في الجزء العلوي من التعليمات البرمجية التي تم إنشاؤها بواسطة dep3 المشروع ، سنجد على الأرجح أوامر الاستيراد التي تبدو كما يلي

 import {...} from '/vendor-5e6f.mjs'; 

الآن دعونا نفكر فيما سيحدث إذا تغيرت محتويات جزء vendor .

إذا حدث هذا ، فسيتم أيضًا تغيير التجزئة في اسم الملف المقابل. ونظرًا لأن رابط اسم هذا الملف موجود في أوامر الاستيراد الخاصة dep2 و dep3 ، dep3 تغيير أوامر الاستيراد هذه:

 -import {...} from '/vendor-5e6f.mjs'; +import {...} from '/vendor-d4a1.mjs'; 

ومع ذلك ، نظرًا لأن أوامر الاستيراد هذه جزء من محتويات dep2 و dep3 ، فإن تغييرها يعني أن تجزئة محتويات ملفات dep3 و dep3 أيضًا. وهذا يعني أن أسماء هذه الملفات ستتغير أيضًا.

لكن هذا لا ينتهي هناك. نظرًا لأن الجزء main يستورد الأجزاء dep2 و dep3 ، وقد تغيرت أسماء الملفات الخاصة بهما ، فإن أوامر الاستيراد main تتغير أيضًا:

 -import {...} from '/dep2-3c4d.mjs'; +import {...} from '/dep2-2be5.mjs'; -import {...} from '/dep3-d4e5.mjs'; +import {...} from '/dep3-3c6f.mjs'; 

أخيرًا ، نظرًا لأن محتويات الملف main قد تغيرت ، فسيتعين أيضًا تغيير اسم هذا الملف.

هذه هي الطريقة التي سيبدو بها مخطط التبعية الآن.


الوحدات النمطية في شجرة التبعية المتأثرة بتغيير واحد في رمز إحدى العقد الورقية للشجرة

يوضح هذا المثال كيف أدى تغيير رمز صغير تم إجراؤه في ملف واحد فقط إلى إبطال ذاكرة التخزين المؤقت لـ 80٪ من أجزاء الحزمة.

على الرغم من أن الحقيقة هي أن جميع التغييرات لا تؤدي إلى مثل هذه النتائج المحزنة (على سبيل المثال ، يؤدي إبطال ذاكرة التخزين المؤقت لعقدة الأوراق إلى إبطال ذاكرة التخزين المؤقت لجميع العقد وصولاً إلى الجذر ، ولكن إبطال ذاكرة التخزين المؤقت الجذر لا يؤدي إلى إبطال المتتالية ، والوصول إلى ورقة الصيد) ، في عالم مثالي لن نضطر إلى التعامل مع أي عمليات إبطال لذاكرة التخزين المؤقت غير ضرورية.

يقودنا هذا إلى السؤال التالي: "هل من الممكن الحصول على فوائد الموارد الثابتة والتخزين المؤقت طويل الأجل ، بينما لا نعاني من إبطال ذاكرة التخزين المؤقت المتتالية؟"

نهج حل المشكلات


المشكلة في تجزئة محتويات الملفات في أسماء الملفات ، من وجهة نظر تقنية ، ليست أن التجزئات موجودة في الأسماء. يكمن في حقيقة أن هذه التجزئة تظهر داخل ملفات أخرى. نتيجة لذلك ، يتم تعطيل ذاكرة التخزين المؤقت لهذه الملفات عند تغيير التجزئة في أسماء الملفات التي تعتمد عليها.

يتمثل حل هذه المشكلة في استخدام لغة المثال أعلاه لتمكين استيراد جزء vendor بواسطة الأجزاء dep2 و dep3 دون تحديد معلومات الإصدار لملف جزء vendor . عند القيام بذلك ، يجب عليك التأكد من صحة إصدار vendor الذي تم تنزيله ، مع مراعاة الإصدارات الحالية من dep2 و dep3 .

كما اتضح ، هناك عدة طرق لتحقيق هذا الهدف:

  • استيراد البطاقات.
  • عمال الخدمة.
  • البرامج النصية الأصلية لموارد التحميل.

النظر في هذه الآليات.

النهج رقم 1: استيراد البطاقات


تعتبر استيراد الخرائط هي أبسط الحلول لإبطال ذاكرة التخزين المؤقت المتتالية. بالإضافة إلى ذلك ، هذه الآلية أسهل في التنفيذ. ولكن ، لسوء الحظ ، يتم دعمه فقط في Chrome (يجب أيضًا تمكين هذه الميزة بشكل صريح).

رغم ذلك ، أريد أن أبدأ بقصة استيراد البطاقات ، لأنني متأكد من أن هذا القرار سيصبح الأكثر شيوعًا في المستقبل. بالإضافة إلى ذلك ، سيساعد وصف العمل باستخدام بطاقات الاستيراد في شرح ميزات الطرق الأخرى لحل مشكلتنا.

يتألف استخدام خرائط الاستيراد لمنع إبطال ذاكرة التخزين المؤقت المتتالية من ثلاث خطوات.

الخطوة 1


تحتاج إلى تكوين المجمّع بحيث لا يتضمن تجزئة محتويات الملفات في أسمائهم عند إنشاء المشروع.

إذا قمت بتجميع مشروع تم عرض وحداته في الرسم التخطيطي من المثال السابق ، دون تضمين تجزئة محتوياتها في أسماء الملفات ، فإن الملفات في دليل إخراج المشروع ستبدو كما يلي:

 dep1.mjs dep2.mjs dep3.mjs main.mjs vendor.mjs 

لن تتضمن أوامر الاستيراد في الوحدات النمطية المقابلة أيضًا التجزئة:

 import {...} from '/vendor.mjs'; 

الخطوة 2


تحتاج إلى استخدام أداة مثل rev-hash ، واستخدامها لإنشاء نسخة من كل ملف مع إضافة علامة إلى اسمه تشير إلى إصدار محتوياته.
بعد الانتهاء من هذا الجزء من العمل ، يجب أن تبدو محتويات دليل المخرجات كما هو موضح أدناه (لاحظ أنه يوجد الآن خياران لكل ملف):

 dep1-b2c3.mjs", dep1.mjs dep2-3c4d.mjs", dep2.mjs dep3-d4e5.mjs", dep3.mjs main-1a2b.mjs", main.mjs vendor-5e6f.mjs", vendor.mjs 

الخطوة 3


تحتاج إلى إنشاء كائن JSON يقوم بتخزين معلومات حول مراسلات كل ملف باسمه لا توجد علامة تجزئة لكل ملف يوجد تجزئة باسمه. يجب إضافة هذا الكائن إلى قوالب HTML.

كائن JSON هذا عبارة عن مخطط استيراد. إليك ما قد يبدو عليه:

 <script type="importmap"> {  "imports": {    "/main.mjs": "/main-1a2b.mjs",    "/dep1.mjs": "/dep1-b2c3.mjs",    "/dep2.mjs": "/dep2-3c4d.mjs",    "/dep3.mjs": "/dep3-d4e5.mjs",    "/vendor.mjs": "/vendor-5e6f.mjs",  } } </script> 

بعد ذلك ، كلما رأى المستعرض أمر استيراد الملف الموجود على العنوان المطابق لأحد مفاتيح خريطة الاستيراد ، يقوم المستورد باستيراد الملف الذي يطابق قيمة المفتاح.

إذا استخدمت خريطة الاستيراد هذه كمثال ، فيمكنك معرفة أن أمر الاستيراد الذي يشير إلى ملف /vendor.mjs سيقوم بالفعل بالاستعلام عن ملف /vendor-5e6f.mjs :

 //    `/vendor.mjs`,  `/vendor-5e6f.mjs`. import {...} from '/vendor.mjs'; 

هذا يعني أن الكود المصدري للوحدات النمطية يمكن بسهولة الرجوع إلى أسماء ملفات الوحدات التي لا تحتوي على تجزئات ، وسوف يقوم المتصفح دائمًا بتنزيل الملفات التي تحتوي أسماؤها على معلومات حول إصدارات محتوياتها. ونظرًا لعدم وجود تجزئات في الكود المصدري للوحدات النمطية (وهي موجودة فقط في خريطة الاستيراد) ، فإن التغييرات في هذه التجزئات لن تؤدي إلى إبطال الوحدات النمطية بخلاف تلك التي تغيرت محتوياتها بالفعل.

ربما تتساءل الآن عن سبب إنشاء نسخة من كل ملف بدلاً من مجرد إعادة تسمية الملفات. يعد هذا ضروريًا لدعم المتصفحات التي لا يمكنها العمل مع استيراد الخرائط. في المثال السابق ، لن ترى مثل هذه المتصفحات سوى ملف /vendor.mjs وتنزيل هذا الملف ببساطة ، كما يفعلون عادة ، ويواجهون بنيات مماثلة. نتيجة لذلك ، اتضح أن كلا الملفين يجب أن يتواجدا على الخادم.

إذا كنت ترغب في رؤية استيراد الخرائط قيد التنفيذ ، فإليك مجموعة من الأمثلة التي توضح جميع الطرق لحل مشكلة إبطال ذاكرة التخزين المؤقت المتتالية الموضحة في هذه المقالة. يمكنك أيضًا إلقاء نظرة على تكوين مجموعة المشروع ، إذا كنت مهتمًا بمعرفة كيفية إنشاء خريطة الاستيراد وتجزئة الإصدار لكل ملف.

أن تستمر ...

أعزائي القراء! هل أنت على علم بإلغاء ذاكرة التخزين المؤقت المتتالية؟


Source: https://habr.com/ru/post/ar472860/


All Articles