سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاس

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاس

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

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

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

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

نحن نحدد المهمة

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

كنت مهتمًا بشكل أساسي بكفاءة الموارد وما يسمى بالواجهة السلسة. كل يوم على طريق المنزل-العمل-المنزل ، رأيت هاتفي القديم يحاول بشكل محموم عرض 20 عنوانًا في الخلاصة. بدا الأمر كالتالي:

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاسواجهة هبر المتنقلة قبل إعادة البناء

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

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاسمخطط SSR-CSR القديم. التفويض ممكن فقط في المرحلتين C3 و C4 ، عندما لا تكون Node JS مشغولة بإنشاء HTML ويمكنها وكيل طلبات API.

تم وصف الهندسة المعمارية الخاصة بنا في ذلك الوقت بدقة شديدة من قبل أحد مستخدمي هبر:

النسخة المحمولة هراء. أنا أتحدث كما هو. مزيج رهيب من SSR مع CSR.

كان علينا الاعتراف بذلك ، مهما كان حزينًا.

لقد اكتشفت الخيارات ، ووضعت لنفسي تذكرة في Jira مع وصف على مستوى "الآن الأمر سيء ، افعل ذلك بشكل صحيح" وقمت بتحليل المهمة بضربات عريضة:

  • إعادة استخدام البيانات
  • تقليل عدد عمليات إعادة الرسم ،
  • استبعاد الطلبات المكررة ،
  • اجعل عملية التمهيد أكثر وضوحًا.

إعادة استخدام البيانات

من الناحية النظرية ، تم تصميم العرض من جانب الخادم لحل مشكلتين: ألا تعاني من قيود محركات البحث من حيث فهرسة SPA وتحسين المقياس FMP (يتفاقم حتما TTI). في السيناريو الكلاسيكي ، وهو نهائي تمت صياغته في Airbnb في عام 2013 year (لا يزال على Backbone.js) ، SSR هو نفس تطبيق JS المتماثل الذي يعمل في العقدة. يقوم الخادم ببساطة بإرجاع التنسيق الذي تم إنشاؤه كاستجابة للطلب. ثم تتم معالجة الجفاف من جانب العميل ، ثم يعمل كل شيء بدون إعادة تحميل الصفحة. بالنسبة إلى Habr ، بالإضافة إلى العديد من الموارد الأخرى ذات المحتوى النصي ، يعد عرض الخادم عنصرًا مهمًا في بناء علاقات ودية مع محركات البحث.

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

لماذا؟ لا توجد إجابة دقيقة لهذا السؤال. إما أنهم لم يرغبوا في زيادة حجم الاستجابة من الخادم ، أو بسبب مجموعة من المشكلات المعمارية الأخرى ، أو أنهم ببساطة لم ينطلقوا. بطريقة أو بأخرى ، يبدو أن إلقاء الحالة وإعادة استخدام كل ما فعله الخادم أمر مناسب ومفيد للغاية. المهمة تافهة في الواقع - يتم حقن الدولة فقط إلى سياق التنفيذ ، ويضيفه Vue تلقائيًا إلى التخطيط المُنشأ كمتغير عام: window.__INITIAL_STATE__.

إحدى المشكلات التي نشأت هي عدم القدرة على تحويل الهياكل الدورية إلى JSON (مرجع دائري) ؛ تم حلها ببساطة عن طريق استبدال هذه الهياكل بنظيراتها المسطحة.

بالإضافة إلى ذلك ، عند التعامل مع محتوى UGC ، يجب أن تتذكر أنه يجب تحويل البيانات إلى كيانات HTML حتى لا تكسر HTML. لهذه الأغراض ، نستخدم he.

التقليل من عمليات إعادة الرسم

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

كان هناك حلان فقط للمشكلة:

  • إرفاق بيانات التفويض بطلبات interserver ؛
  • قسّم طبقات Node JS إلى حالتين منفصلتين.

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

كيف تختار؟ غالبًا ما يتحرك هبر على طول الطريق الأقل مقاومة. بشكل غير رسمي ، هناك رغبة عامة معينة لتقليل الدورة من الفكرة إلى النموذج الأولي إلى الحد الأدنى. يذكرنا نموذج الموقف تجاه المنتج إلى حد ما بمسلمات booking.com ، مع الاختلاف الوحيد هو أن هابر يأخذ ملاحظات المستخدم بجدية أكبر ويثق بك كمطور لاتخاذ مثل هذه القرارات.

باتباع هذا المنطق ورغبتي في حل المشكلة بسرعة ، اخترت المتغيرات العالمية. وكما يحدث غالبًا ، يتعين عليك دفع ثمنها عاجلاً أم آجلاً. لقد دفعنا على الفور تقريبًا: كتبنا: لقد عملنا في عطلة نهاية الأسبوع ، وقمنا بفرز العواقب تشريح وبدأت بتقسيم الخادم إلى قسمين. كان الخطأ غبيًا جدًا ، ولم يكن من السهل تكرار الخطأ بمشاركتها. ونعم ، إنه عار على هذا ، ولكن على أي حال ، التعثر والتأوه ، لا يزال PoC الخاص بي مع المتغيرات العالمية قيد الإنتاج ويعمل بنجاح كبير تحسباً للانتقال إلى بنية جديدة "لمدة يومين". كانت هذه خطوة مهمة ، لأنه تم تحقيق الهدف رسميًا - تعلمت SSR إعطاء صفحة جاهزة تمامًا للاستخدام ، وأصبحت واجهة المستخدم أكثر استرخاءً.

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاسواجهة الجوال هبر بعد المرحلة الأولى من إعادة الهيكلة

في النهاية ، تؤدي بنية SSR-CSR لإصدار الهاتف المحمول إلى هذه الصورة:

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاسمخطط "ثنائي العقد" SSR-CSR. Node JS API جاهز دائمًا للإدخال / الإخراج غير المتزامن ولا يتم حظره بواسطة وظيفة SSR ، نظرًا لأن الأخير في مثيل منفصل. سلسلة الطلب # 3 ليست مطلوبة.

القضاء على الطلبات المكررة

بعد التلاعب ، توقف العرض الأولي للصفحة عن إثارة الصرع. لكن الاستخدام الإضافي لـ Habr في وضع SPA كان لا يزال محيرًا.

نظرًا لأن تدفق المستخدم يعتمد على انتقالات النموذج قائمة المقالات → المادة → التعليقات والعكس صحيح ، كان من المهم تحسين استهلاك الموارد لهذه السلسلة في المقام الأول.

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاسالعودة إلى آخر تغذية يثير طلب جديد للبيانات

لم يكن لديك لحفر عميق. يُظهر تسجيل الشاشة أعلاه أن التطبيق يعيد طلب قائمة المقالات عند التمرير للخلف ، وأثناء الطلب لا نرى المقالات ، مما يعني أن البيانات السابقة تختفي في مكان ما. يبدو أن مكون قائمة المقالات يستخدم الولاية المحلية ويفقدها عند التدمير. في الواقع ، استخدم التطبيق حالة عالمية ، ولكن تم إنشاء بنية Vuex "على الجبهة": يتم إرفاق الوحدات بالصفحات ، والتي يتم إرفاقها بدورها بالمسارات. علاوة على ذلك ، جميع الوحدات "لمرة واحدة" - كل زيارة تالية للصفحة تعيد كتابة الوحدة بأكملها:

ArticlesList: [
  { Article1 },
  ...
],
PageArticle: { ArticleFull1 },

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

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

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

ArticlesList: {
  ROUTE_FEED: [ 
    { Article1 },
    ...
  ],
  ROUTE_ALL: [ 
    { Article2 },
    ...
  ],
}

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

ArticlesIds: {
  ROUTE_FEED: [ '1', ... ],
  ROUTE_ALL: [ '1', '2', ... ],
},
ArticlesList: {
  '1': { Article1 }, 
  '2': { Article2 },
  ...
}

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

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

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

جعل التحميل أكثر متعة

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

سجلات مطور الواجهة الأمامية Habr: إعادة البناء والانعكاس
habraloading

تعكس

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

بعد إصدار كل هذه التغييرات ، تلقينا ردود فعل إيجابية ، والتي كانت لطيفة للغاية. إنه مصدر إلهام. شكرًا لك! أكتب أكثر.

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

المصدر: www.habr.com

إضافة تعليق