في السنوات الأخيرة ، ظهرت الكثير من
الأشياء الجديدة على
instagram.com . كثير على سبيل المثال - أدوات سرد القصص والمرشحات والأدوات الإبداعية والإشعارات والرسائل المباشرة. ومع ذلك ، مع نمو المشروع ، أعطى كل هذا تأثيرًا جانبيًا محزنًا ، وهو أن أداء instagram.com بدأ في الانخفاض. خلال العام الماضي ، بذل فريق تطوير Instagram جهودًا متواصلة لإصلاح ذلك. أدى ذلك إلى حقيقة أن إجمالي وقت التحميل لخلاصة Instagram (صفحة الخلاصة) انخفض بنسبة 50٪ تقريبًا.

ننشر اليوم ترجمة للمواد الأولى من سلسلة من المقالات المكرسة لقصة كيفية تسريع instagram.com.
حول تحسين أداء مشاريع الويب
تحسين الأداء خلال العام الماضي (خلاصة Instagram ، عرض تم القياس ، مللي ثانية)من أهم الطرق لتحسين أداء تطبيقات الويب تحديد أولويات موارد التحميل والمعالجة بشكل صحيح وتقليل وقت توقف المتصفح أثناء تحميل الصفحة. في حالتنا ، أثبتت العديد من عمليات التحسين هذه أنها أكثر فاعلية من تقليل حجم الشفرة. كقاعدة عامة ، لم يكن لدينا أي شكاوى حول حجم الرمز. كان مضغوطًا بما فيه الكفاية. بدأت أبعادها تزعجنا فقط بعد إجراء الكثير من التحسينات الصغيرة على المشروع (نخطط أيضًا للتحدث عن تحسين حجم الرمز). بالإضافة إلى ذلك ، كان لهذه التحسينات تأثير أقل على عملية تطوير المشروع. لقد تطلبوا تغييرات أقل في الكود وأقل إعادة إعمار. ونتيجة لذلك ، ركزنا في البداية جهودنا على وجه التحديد على هذا المجال ، بدءًا من موارد التحميل المسبق.
قصة حول التحميل المسبق للصور ورمز JavaScript والمواد اللازمة لاستكمال الاستعلامات ، وكذلك أين يجب توخي الحذر
كان المبدأ العام لتحسيناتنا هو إعلام المستعرض في أسرع وقت ممكن بالموارد اللازمة لتحميل الصفحة. نحن ، كمطورين للمشروعات ، في كثير من الحالات كنا نعرف مقدما ما هو المطلوب بالضبط لذلك لكن لا يمكن للمتصفح معرفة ذلك حتى يتم تحميل جزء معين من مواد الصفحة ومعالجتها. تضمنت الموارد المعنية ، في معظمها ، الموارد التي تم تحميلها ديناميكيًا باستخدام JavaScript (على سبيل المثال ، البرامج النصية والصور والمواد اللازمة لتنفيذ طلبات XHR). الحقيقة هي أن المستعرض لا يمكنه اكتشاف هذه الموارد التابعة حتى يوزع وينفذ بعض شفرة JavaScript.
بدلاً من الانتظار حتى يجد المتصفح نفسه هذه الموارد ، يمكننا أن نعطيه تلميحًا ، يمكن أن يبدأ بعد ذلك في تنزيلها على الفور. لقد فعلنا ذلك باستخدام سمات HTML
preload
. يبدو شيء مثل هذا:
<link rel="preload" href="my-js-file.js" as="script" type="text/javascript" />
نحن نستخدم تلميحات مشابهة لنوعين من الموارد على مسارات تحميل الصفحات الحرجة. يتم تحميل شفرة JavaScript هذه ديناميكيًا والمواد المحملة ديناميكيًا من GraphQL XHR- طلبات البيانات. البرامج النصية المحملة بشكل حيوي هي البرامج النصية التي يتم تحميلها باستخدام بنيات
import('...')
النموذج
import('...')
لطرق عميل محددة. نحتفظ بقائمة المراسلات بنقاط إدخال الخادم ونصوص مسار العميل. نتيجة لذلك ، عندما نتلقى ، على الخادم ، طلبًا لتحميل الصفحة ، فإننا نعرف عن البرامج النصية التي يجب على العميل تنزيل مساراتها. نتيجة لذلك ، يمكننا ، عند إنشاء رمز HTML للصفحة ، إضافة تلميحات مناسبة إليها.
على سبيل المثال ، عند العمل مع نقطة إدخال
FeedPage
فإننا نعلم أن موجه العميل سيكمل أخيرًا طلب تنزيل
FeedPageContainer.js
. نتيجة لذلك ، يمكننا إضافة البناء التالي إلى رمز الصفحة:
<link rel="preload" href="/static/FeedPageContainer.js" as="script" type="text/javascript" />
وبالمثل ، إذا علمنا أنه من المخطط تنفيذ استعلام GraphQL لنقطة إدخال صفحة معينة ، فهذا يعني أننا نحتاج إلى تحميل المواد مسبقًا لتسريع تنفيذ هذا الاستعلام. هذا مهم بشكل خاص بسبب حقيقة أن تنفيذ استعلامات GraphQL هذه يستغرق أحيانًا الكثير من الوقت ، ولا يمكن تقديم الصفحة حتى يتم إرجاع نتائج الاستعلام. لهذا السبب ، نحتاج إلى جعل الخادم في أقرب وقت ممكن يشارك في تشكيل استجابات لهذه الطلبات.
<link rel="preload" href="/graphql/query?id=12345" as="fetch" type="application/json" />
التغييرات في ميزات تحميل الصفحة ملحوظة بشكل خاص على الاتصالات البطيئة. من خلال محاكاة اتصال 3G السريع (أول مخطط شلال أدناه ، والذي يوضح الموقف عند عدم استخدام تحميل المورد) ، يمكننا أن نرى أن تحميل
FeedPageContainer.js
وتنفيذ استعلام GraphQL المرتبط به يبدأ فقط بعد تحميل
Consumer.js
. ومع ذلك ، في حالة استخدام التحميل
FeedPageContainer.js
، يمكن بدء تحميل البرنامج النصي
FeedPageContainer.js
وتنفيذ استعلام GraphQL فور توفر صفحة HTML. هذا ، بالإضافة إلى ذلك ، يقلل من الوقت اللازم لتنزيل أي نصوص ثانوية تستخدم آليات التحميل البطيئة. هنا ،
FeedSidebarContainer.js
و
ActivityFeedBox.js
(التي تعتمد على
FeedPageContainer.js
) في التحميل فورًا تقريبًا بعد معالجة
Consumer.js
.
التحميل المسبق غير مستخدمالتحميل المسبق المستخدمةفوائد تحديد أولويات التحميل المسبق
بالإضافة إلى استخدام سمة
preload
لبدء تحميل الموارد بشكل أسرع ، فإن استخدام هذه الآلية له ميزة أخرى. يتكون في زيادة أولوية الشبكة من تحميل البرنامج النصي غير المتزامن. يصبح هذا مهمًا عند استخدام البرامج النصية المحملة بشكل غير متزامن في المسارات الحرجة لتحميل الصفحات ، حيث يتم تحميلها افتراضيًا بأولوية منخفضة بشكل افتراضي. نتيجةً لذلك ، ستكون أولوية طلبات XHR والصور المتعلقة بمنطقة الصفحة المرئية للمستخدمين أعلى من المواد الموجودة خارج منطقة العرض. ولكن هذا يمكن أن يؤدي إلى مواقف يتم فيها حظر البرامج النصية الهامة اللازمة لتقديم الصفحة أو إجبارها على مشاركة النطاق الترددي مع الموارد الأخرى. إذا كنت مهتمًا ،
فإليك حساب مفصل لأولويات موارد Chrome. الاستخدام المدروس لآلية التحميل المسبق (سنتحدث عن هذا أدناه) يمنح المطور مستوى معينًا من التحكم في كيفية إعطاء المتصفح الأولوية لعملية التحميل الأولي للصفحة. ينطبق هذا بشكل خاص على الحالات التي يعرف فيها المطور الموارد المهمة للعرض الصحيح للصفحة.
التحميل المسبق قضايا تحديد الأولويات
تكمن مشكلة تحميل الموارد مسبقًا على وجه التحديد في أنها تعطي المطور قوة إضافية للتأثير على أولوية تحميل الموارد. هذا يعني أن المطور لديه مسؤولية أكبر عن تحديد الأولويات المناسبة. على سبيل المثال ، عند اختبار موقع في المناطق التي تكون فيها سرعة شبكات المحمول وشبكة WiFi منخفضة جدًا ، ولوحظ فيها نسبة كبيرة من فقد الحزمة ، لاحظنا أن الطلب تم تنفيذه أثناء معالجة
<link rel="preload" as="script">
يحظى
<link rel="preload" as="script">
بأولوية أعلى من الطلب الذي يتم تنفيذه عند معالجة
<script />
لحزم JavaScript المستخدمة في مسارات عرض الصفحة الحرجة. هذا يؤدي إلى زيادة في إجمالي وقت تحميل الصفحة.
مصدر هذه المشكلة هو كيف وضعنا علامات التحميل المسبق على صفحاتنا. أي ، قمنا بإضافة تلميحات التحميل المسبق فقط للحزم ، والتي تعد جزءًا من الصفحة الحالية ، والتي كنا سنقوم بتحميلها بشكل غير متزامن مع جهاز توجيه العميل.
<link rel="preload" href="SomeConsumerRoute.js" as="script" /> <link rel="preload" href="..." as="script" /> ... <script src="Common.js" type="text/javascript"></script> <script src="Consumer.js" type="text/javascript"></script>
على سبيل المثال ، في صفحة تسجيل الخروج ، نقوم بتحميل
SomeConsumerRoute.js
على
Common.js
و
Consumer.js
، وبما أن موارد التحميل المسبق يتم تحميلها بأولوية أعلى ، ولكن لا يتم تحليلها ، يؤدي هذا إلى حظر تحليل
Common.js
وتحليل
Consumer.js
عثر فريق تطوير Chrome Data Saver على مشكلة تحميل مسبق مشابه
ووصفوا حلهم لهذه المشكلة. في حالتهم ، تقرر دائمًا وضع إنشاءات لتحميل الموارد غير المتزامنة مسبقًا بعد العلامات
<script />
لتلك الموارد التي تستخدم هذه الموارد غير المتزامنة. قررنا تحميل جميع البرامج النصية مسبقًا ووضع الإنشاءات المقابلة في الكود بالترتيب الذي ستكون هناك حاجة إليه. هذا يتيح لنا الفرصة لبدء التحميل المسبق لجميع موارد البرنامج النصي للصفحة في أسرع وقت ممكن. يتضمن ذلك علامات لتحميل البرامج النصية بشكل متزامن لا يمكن إضافته إلى HTML حتى يتم وضع بيانات خادم محددة على الصفحة. هذا يتيح لنا التحكم في ترتيب تحميل البرامج النصية.
هنا هو الترميز الذي يسبق تحميل جميع حزم جافا سكريبت.
<link rel="preload" href="Common.js" as="script" /> <link rel="preload" href="Consumer.js" as="script" /> <link rel="preload" href="SomeConsumerRoute.js" as="script" /> ... <script src="Common.js" type="text/javascript"></script> <script src="Consumer.js" type="text/javascript"></script> <script src="SomeConsumerRoute.js" type="text/javascript" async></script>
التحميل المسبق للصورة
واحدة من مجالات العمل الرئيسية في instagram.com هي الخلاصة. إنها صورة وفيديو يدعم التمرير اللانهائي. نحن ملء هذه الصفحة مثل هذا. أولاً ، قم بتنزيل المجموعة الأولية من المنشورات ، ثم أثناء قيام المستخدم بالتمرير بالصفحة ، قم بتحميل مجموعات إضافية من المواد. ومع ذلك ، لا نريد أن ينتظر المستخدم تحميل مواد جديدة في كل مرة يصل فيها إلى أسفل الشريط. نتيجة لذلك ، من أجل تسهيل العمل مع هذه الصفحة ، نقوم بتحميل مجموعات جديدة من المواد قبل أن يصل المستخدم إلى نهاية الشريط.
في الممارسة العملية ، هذه ليست مهمة سهلة لعدة أسباب:
- نحتاج إلى تنزيل مواد غير مرئية للمستخدم ، حتى لا تأخذ موارد الشبكة والمعالج من المواد التي يشاهدها.
- لا نريد نقل البيانات غير الضرورية عبر الشبكة ، ونحاول جاهدين تحميل المنشورات التي قد لا يراها المستخدم. ولكن ، من ناحية أخرى ، إذا لم نقم بتحميل كمية كافية من المواد مسبقًا ، فغالبًا ما يعني ذلك أن المستخدم "سيصطدم" بنهاية الشريط.
- تم تصميم مشروع instagram.com للعمل على أجهزة مختلفة وعلى شاشات بأحجام مختلفة. نتيجة لذلك ، نعرض الصور في الشريط باستخدام سمة
srcset
<img>
. تتيح هذه السمة للمتصفح ، بالنظر إلى حجم الشاشة ، تحديد دقة الصورة التي يجب استخدامها. هذا يعني أنه ليس من السهل علينا تحديد دقة الصور التي يجب تنزيلها مسبقًا. بالإضافة إلى ذلك ، هناك خطر من التحميل المسبق للصور التي لن يستخدمها المتصفح.
كان الأسلوب الذي اعتدنا عليه لحل هذه المشكلة هو إنشاء مجموعة من مهمة تحديد الأولويات ، وهي المسؤولة عن طابور المهام غير المتزامنة (في هذه الحالة ، هذه مهام لتحميل المجموعة التالية من المنشورات لإخراجها في الشريط). يتم وضع مهمة مشابهة في البداية في قائمة
requestIdleCallback
idle
(هنا
requestIdleCallback
استخدام
requestIdleCallback
). هذا يعني أن تنفيذ هذه المهمة لن يبدأ طالما أن المتصفح مشغول بأي عمل مهم آخر. ومع ذلك ، إذا قام المستخدم بالتمرير الصفحة قريبة بما فيه الكفاية إلى المكان الذي تنتهي فيه المجموعة الحالية من المنشورات التي تم تنزيلها ، فإن أولوية مهمة تحميل المواد المسبقة هذه تتغير إلى
high
. يتم ذلك عن طريق إلغاء رد الاتصال الاحتياطي ، وبعد ذلك تبدأ عملية التحميل المسبق على الفور.
في بداية الشريط وفي منتصفه ، تكون مهمة التحميل المسبق للبيانات في وضع الخمول ذات الأولوية ، وفي نهاية الشريط ، تكون الأولوية عاليةبعد اكتمال تنزيل بيانات JSON للمجموعة التالية من المنشورات ، فإننا ننتظر مهمة خلفية متكررة لتحميل الصور مسبقًا من هذه المجموعة. يتم تنفيذ تحميل الصورة مسبقًا بالترتيب الذي يتم عرض المنشورات به في الخلاصة وليس بالتوازي. يتيح لنا ذلك تحديد أولويات مهام تحميل البيانات وعرض الصور للمنشورات الأقرب إلى المكان على الصفحة التي يراها المستخدم. لتنزيل صور بالحجم الصحيح ، نستخدم مكون وسائط مخفي ، تتوافق معاملاته مع معلمات الشريط الحالي. يوجد داخل هذا المكون عنصر
<img>
يستخدم سمة
srcset
، وهو نفس العنصر المستخدم لعرض المنشورات الحقيقية في الخلاصة. هذا يعني أنه يمكننا تزويد المستعرض بالقدرة على اتخاذ القرارات بشأن أي الصور سيتم تحميلها مسبقًا. نتيجة لذلك ، سيستخدم المتصفح نفس المنطق عند عرض الصور التي استخدمها عند تحميلها مسبقًا. وهذا يعني أيضًا أننا ، باستخدام مكون وسائط مشابه ، يمكننا تحميل الصور مسبقًا إلى مناطق أخرى من الموقع. مثل صفحات ملف تعريف المستخدم.
أدى التأثير الكلي للتحسينات المذكورة أعلاه إلى انخفاض بنسبة 25 ٪ في الوقت اللازم لتحميل الصور. نحن نتحدث عن طول الفترة الزمنية بين لحظة إضافة رمز المنشور إلى DOM والوقت الذي يتم فيه تحميل صورة المنشور وعرضها. بالإضافة إلى ذلك ، أدى ذلك إلى انخفاض بنسبة 56٪ في الوقت الذي قضاه المستخدمون ، بعد وصولهم إلى نهاية الخلاصة ، في انتظار تنزيل مواد جديدة.
أعزائي القراء! هل تستخدم آليات تحميل البيانات لتحسين مشاريع الويب الخاصة بك؟
