لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتاب

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتاب

من همیشه به چگونگی ساختار Habr از داخل، چگونگی ساختار گردش کار، نحوه ساختار ارتباطات، چه استانداردهایی استفاده می شود و به طور کلی کد چگونه در اینجا نوشته می شود، علاقه مند بوده ام. خوشبختانه چنین فرصتی به دست آوردم، زیرا به تازگی عضوی از تیم هابرا شدم. با استفاده از مثال بازسازی کوچک نسخه موبایل، سعی می کنم به این سوال پاسخ دهم: کار کردن در اینجا در جلو چگونه است. در برنامه: Node، Vue، Vuex و SSR با سس از یادداشت هایی در مورد تجربه شخصی در Habr.

اولین چیزی که باید در مورد تیم توسعه بدانید این است که تعداد کمی از ما وجود دارد. کافی نیست - اینها سه جلو، دو عقب و رهبری فنی همه هابر - باکسلی هستند. البته یک تستر، یک طراح، سه وادیم، یک جارو معجزه‌آسا، یک متخصص بازاریابی و سایر بومبوروم‌ها نیز وجود دارد. اما تنها شش مشارکت کننده مستقیم در منابع هابر وجود دارد. این بسیار نادر است - پروژه ای با مخاطبان چند میلیون دلاری که از بیرون مانند یک شرکت غول پیکر به نظر می رسد، در واقع بیشتر شبیه یک استارتاپ دنج با صاف ترین ساختار سازمانی ممکن است.

مانند بسیاری دیگر از شرکت‌های فناوری اطلاعات، هابر ایده‌های چابک، شیوه‌های CI، و این همه است. اما با توجه به احساس من، هابر به عنوان یک محصول بیشتر در حال توسعه امواج است تا پیوسته. بنابراین، برای چندین سرعت متوالی، ما با پشتکار چیزی را کدنویسی می‌کنیم، طراحی و بازطراحی می‌کنیم، چیزی را می‌شکنیم و تعمیر می‌کنیم، بلیت‌ها را حل می‌کنیم و بلیط‌های جدید ایجاد می‌کنیم، قدم بر روی یک چنگک می‌زنیم و به پاهای خودمان شلیک می‌کنیم تا در نهایت ویژگی را در آن آزاد کنیم. تولید و سپس آرامش خاصی فرا می رسد، دوره ای از توسعه مجدد، زمان انجام آنچه در ربع «مهم-نه فوری» است.

دقیقاً این سرعت "خارج از فصل" است که در زیر مورد بحث قرار خواهد گرفت. این بار شامل بازسازی نسخه موبایل Habr بود. به طور کلی، این شرکت امید زیادی به آن دارد و در آینده باید جایگزین کل باغ وحش تجسم هابر شود و به یک راه حل بین پلتفرمی جهانی تبدیل شود. روزی طرح تطبیقی، PWA، حالت آفلاین، سفارشی سازی کاربر و بسیاری چیزهای جالب دیگر وجود خواهد داشت.

بیایید تکلیف را تعیین کنیم

یک بار، در یک استندآپ معمولی، یکی از جلو در مورد مشکلات در معماری اجزای نظرات نسخه موبایل صحبت کرد. با توجه به این موضوع، یک نشست خرد در قالب روان درمانی گروهی برگزار کردیم. همه به نوبت می گفتند کجا درد دارد، همه چیز را روی کاغذ می نوشتند، همدردی می کردند، می فهمیدند، جز اینکه هیچکس کف نمی زد. نتیجه لیستی از 20 مشکل بود که نشان داد موبایل هابر هنوز مسیری طولانی و خاردار برای موفقیت دارد.

من در درجه اول نگران کارایی استفاده از منابع و آنچه که رابط صاف نامیده می شود بودم. هر روز، در مسیر خانه-کار-خانه، تلفن قدیمی ام را می دیدم که ناامیدانه سعی می کند 20 عنوان را در فید نمایش دهد. چیزی شبیه این به نظر می رسید:

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتابرابط موبایل هابر قبل از بازسازی

اینجا چه خبره؟ به طور خلاصه، سرور صفحه HTML را به یکسان به همه ارائه می دهد، صرف نظر از اینکه کاربر وارد شده است یا خیر. سپس سرویس گیرنده JS بارگذاری می شود و اطلاعات لازم را دوباره درخواست می کند، اما برای مجوز تنظیم می شود. یعنی ما در واقع یک کار را دو بار انجام دادیم. رابط سوسو زد و کاربر صد کیلوبایت اضافی را دانلود کرد. در جزئیات همه چیز حتی وحشتناک تر به نظر می رسید.

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتابطرح قدیمی SSR-CSR. مجوز فقط در مراحل C3 و C4 امکان پذیر است، زمانی که Node JS مشغول تولید HTML نیست و می تواند درخواست های پراکسی را به API انجام دهد.

معماری آن زمان ما توسط یکی از کاربران Habr بسیار دقیق توصیف شده است:

نسخه موبایل مزخرف است. همینجوری که هست میگم ترکیبی وحشتناک از SSR و CSR.

باید اعتراف می کردیم، مهم نیست چقدر غم انگیز بود.

من گزینه ها را ارزیابی کردم، یک بلیط در Jira با توصیفی در سطح "حالا بد است، درست انجامش بده" ایجاد کردم و کار را با حرکات گسترده تجزیه کردم:

  • استفاده مجدد از داده ها،
  • به حداقل رساندن تعداد بارگیری مجدد،
  • حذف درخواست های تکراری،
  • فرآیند بارگذاری را واضح تر کنید.

بیایید از داده ها استفاده مجدد کنیم

در تئوری، رندر سمت سرور برای حل دو مشکل طراحی شده است: رنج نبردن از محدودیت های موتور جستجو از نظر نمایه سازی SPA و متریک را بهبود بخشد FMP (به ناچار بدتر می شود TTI). در یک سناریوی کلاسیک که در نهایت در Airbnb در سال 2013 فرموله شد سال (هنوز در Backbone.js)، SSR همان برنامه JS ایزومورفیک است که در محیط Node اجرا می شود. سرور به سادگی طرح تولید شده را به عنوان پاسخ به درخواست ارسال می کند. سپس آبرسانی مجدد در سمت مشتری اتفاق می افتد، و سپس همه چیز بدون بارگیری مجدد صفحه کار می کند. برای Habr، مانند بسیاری از منابع دیگر با محتوای متنی، رندر سرور یک عنصر حیاتی در ایجاد روابط دوستانه با موتورهای جستجو است.

علیرغم این واقعیت که بیش از شش سال از ظهور این فناوری می گذرد و در این مدت واقعاً آب زیادی از زیر پل در دنیای جلویی عبور کرده است، برای بسیاری از توسعه دهندگان این ایده هنوز در راز پنهان است. ما کنار نگذاشتیم و یک برنامه Vue را با پشتیبانی SSR برای تولید عرضه کردیم و یک جزئیات کوچک را از دست دادیم: وضعیت اولیه را برای مشتری ارسال نکردیم.

چرا؟ پاسخ دقیقی برای این سوال وجود ندارد. یا نمی خواستند اندازه پاسخ سرور را افزایش دهند، یا به دلیل یک سری مشکلات معماری دیگر، یا به سادگی انجام نشد. به هر طریقی، بیرون انداختن حالت و استفاده مجدد از هر کاری که سرور انجام داده، کاملا مناسب و مفید به نظر می رسد. کار در واقع بی اهمیت است - حالت به سادگی تزریق می شود در زمینه اجرا، و Vue به طور خودکار آن را به عنوان یک متغیر سراسری به طرح تولید شده اضافه می کند: window.__INITIAL_STATE__.

یکی از مشکلاتی که به وجود آمده عدم توانایی در تبدیل ساختارهای چرخه ای به JSON است (مرجع دایره ای) به سادگی با جایگزینی چنین ساختارهایی با همتایان مسطح خود حل شد.

علاوه بر این، هنگام برخورد با محتوای UGC، باید به خاطر داشته باشید که داده ها باید به موجودیت های HTML تبدیل شوند تا HTML شکسته نشود. برای این اهداف استفاده می کنیم he.

به حداقل رساندن ترسیم مجدد

همانطور که از نمودار بالا می بینید، در مورد ما، یک نمونه Node JS دو عملکرد را انجام می دهد: SSR و "proxy" در API، جایی که مجوز کاربر رخ می دهد. این شرایط باعث می‌شود تا زمانی که کد JS روی سرور اجرا می‌شود، مجوز دادن غیرممکن شود، زیرا گره تک رشته‌ای است و عملکرد SSR همزمان است. یعنی سرور به سادگی نمی تواند درخواست هایی را برای خود ارسال کند در حالی که پشته تماس با چیزی مشغول است. معلوم شد که ما وضعیت را به روز کردیم، اما رابط از تکان خوردن متوقف نشد، زیرا داده های مشتری باید با در نظر گرفتن جلسه کاربر به روز می شد. ما باید به برنامه خود آموزش دهیم که با در نظر گرفتن ورود کاربر، داده های صحیح را در حالت اولیه قرار دهد.

تنها دو راه حل برای مشکل وجود داشت:

  • داده های مجوز را به درخواست های متقابل سرور پیوست کنید.
  • لایه های Node JS را به دو نمونه جداگانه تقسیم کنید.

راه حل اول مستلزم استفاده از متغیرهای سراسری در سرور بود و راه حل دوم مهلت تکمیل کار را حداقل یک ماه تمدید کرد.

چگونه انتخاب کنیم؟ هابر اغلب در مسیری با کمترین مقاومت حرکت می کند. به طور غیر رسمی، تمایل کلی برای کاهش چرخه ایده به نمونه اولیه به حداقل وجود دارد. مدل نگرش نسبت به محصول تا حدودی یادآور اصول booking.com است، با تنها تفاوت این که هابر بازخورد کاربران را بسیار جدی‌تر می‌پذیرد و به شما به عنوان یک توسعه‌دهنده برای اتخاذ چنین تصمیم‌هایی اعتماد دارد.

با پیروی از این منطق و تمایل خودم برای حل سریع مشکل، متغیرهای جهانی را انتخاب کردم. و همانطور که اغلب اتفاق می افتد، دیر یا زود باید هزینه آنها را بپردازید. ما تقریباً بلافاصله پرداخت کردیم: آخر هفته کار کردیم، عواقب را پاک کردیم، نوشتیم پس از مرگ و شروع به تقسیم سرور به دو قسمت کرد. خطا بسیار احمقانه بود و بازتولید باگ مربوط به آن آسان نبود. و بله، برای این مایه شرمساری است، اما به هر نحوی، دست و پا زدن و ناله کردن، PoC من با متغیرهای جهانی با این وجود وارد تولید شد و در حالی که منتظر انتقال به یک معماری جدید "دو گره" است، با موفقیت کار می کند. این یک گام مهم بود، زیرا به طور رسمی هدف محقق شد - SSR یاد گرفت که یک صفحه کاملاً آماده برای استفاده را ارائه دهد و رابط کاربری بسیار آرام‌تر شد.

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتابرابط موبایل هابر پس از اولین مرحله از refactoring

در نهایت، معماری SSR-CSR نسخه موبایل به این تصویر منجر می شود:

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتابمدار SSR-CSR "دو گره". Node JS API همیشه برای I/O ناهمزمان آماده است و توسط تابع SSR مسدود نمی شود، زیرا دومی در یک نمونه جداگانه قرار دارد. زنجیره پرس و جو شماره 3 مورد نیاز نیست.

حذف درخواست های تکراری

پس از انجام دستکاری ها، رندر اولیه صفحه دیگر باعث تحریک صرع نمی شد. اما استفاده بیشتر از Habr در حالت SPA همچنان باعث سردرگمی شد.

از آنجایی که اساس جریان کاربر، انتقال فرم است لیست مقالات → مقاله → نظرات و بالعکس، در وهله اول بهینه سازی مصرف منابع این زنجیره مهم بود.

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتاببازگشت به فید پست یک درخواست داده جدید را تحریک می کند

نیازی به حفاری عمیق نبود. در اسکرین‌کست بالا می‌بینید که برنامه هنگام کشیدن انگشت به عقب، لیست مقالات را دوباره درخواست می‌کند و در حین درخواست، ما مقاله‌ها را نمی‌بینیم، یعنی اطلاعات قبلی در جایی ناپدید می‌شوند. به نظر می‌رسد که مؤلفه فهرست مقاله از یک حالت محلی استفاده می‌کند و در هنگام نابودی آن را از دست می‌دهد. در واقع، این برنامه از یک حالت جهانی استفاده می‌کرد، اما معماری Vuex به طور مستقیم ساخته شد: ماژول‌ها به صفحات گره خورده‌اند، که به نوبه خود به مسیرها گره خورده‌اند. علاوه بر این، همه ماژول ها "یکبار مصرف" هستند - هر بازدید بعدی از صفحه کل ماژول را بازنویسی می کند:

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

در کل ما یک ماژول داشتیم فهرست مقالات، که شامل اشیاء از نوع است مقاله ها و ماژول PageArticle، که یک نسخه توسعه یافته از شی بود مقاله ها ، نوع مقاله کامل. به طور کلی، این پیاده سازی به خودی خود هیچ چیز وحشتناکی ندارد - بسیار ساده است، حتی می توان گفت ساده لوحانه، اما بسیار قابل درک است. اگر هر بار که مسیر را تغییر می‌دهید، ماژول را بازنشانی کنید، حتی می‌توانید با آن زندگی کنید. با این حال، برای مثال، حرکت بین فیدهای مقاله /feed → /all، تضمین شده است که همه چیز مربوط به خوراک شخصی را دور می اندازد، زیرا ما فقط یک مورد داریم فهرست مقالات، که باید داده های جدیدی را در آن قرار دهید. این دوباره ما را به تکرار درخواست ها سوق می دهد.

پس از جمع آوری همه چیزهایی که توانستم در مورد موضوع کشف کنم، یک ساختار دولتی جدید تدوین کردم و آن را به همکارانم ارائه کردم. بحث‌ها طولانی بود، اما در نهایت استدلال‌های مثبت بر تردیدها غلبه کرد و من شروع به اجرا کردم.

منطق یک راه حل به بهترین وجه در دو مرحله آشکار می شود. ابتدا سعی می کنیم ماژول Vuex را از صفحات جدا کنیم و مستقیماً به مسیرها متصل شویم. بله، داده‌های کمی بیشتر در فروشگاه وجود خواهد داشت، دریافت‌کننده‌ها کمی پیچیده‌تر می‌شوند، اما ما مقالات را دوبار بارگذاری نمی‌کنیم. برای نسخه موبایل، این شاید قوی ترین استدلال باشد. چیزی شبیه به این خواهد بود:

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

اما چه می‌شود اگر فهرست‌های مقاله می‌توانند بین مسیرهای متعدد همپوشانی داشته باشند و اگر بخواهیم از داده‌های شی دوباره استفاده کنیم چه می‌شود مقاله ها برای رندر کردن صفحه پست، تبدیل آن به مقاله کامل? در این مورد، منطقی تر است که از چنین ساختاری استفاده کنیم:

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

فهرست مقالات اینجا فقط نوعی مخزن مقالات است. همه مقالاتی که در طول جلسه کاربر دانلود شدند. ما با نهایت دقت با آنها رفتار می کنیم، زیرا این ترافیکی است که ممکن است با درد در جایی در مترو بین ایستگاه ها دانلود شده باشد و قطعاً نمی خواهیم با وادار کردن کاربر به بارگذاری داده هایی که قبلاً دارد، دوباره این درد را برای کاربر ایجاد کنیم. دانلود شده است. یک شی شناسه مقالات به سادگی آرایه ای از شناسه ها (مثل "پیوندها") به اشیا است مقاله ها . این ساختار به شما امکان می دهد از تکرار داده های مشترک در مسیرها و استفاده مجدد از شی جلوگیری کنید مقاله ها هنگام ارائه یک صفحه پست با ادغام داده های گسترده در آن.

خروجی فهرست مقالات نیز شفاف‌تر شده است: مؤلفه تکرارکننده از طریق آرایه با شناسه‌های مقاله تکرار می‌شود و مؤلفه تیزر مقاله را ترسیم می‌کند و شناسه را به‌عنوان پایه ارسال می‌کند و مؤلفه فرزند نیز به نوبه خود داده‌های لازم را از آن بازیابی می‌کند. فهرست مقالات. وقتی به صفحه انتشار می روید، تاریخ موجود را از آن دریافت می کنیم فهرست مقالات، ما یک درخواست برای به دست آوردن داده های از دست رفته و به سادگی آن را به شی موجود اضافه می کنیم.

چرا این رویکرد بهتر است؟ همانطور که در بالا نوشتم، این رویکرد با توجه به داده های دانلود شده ملایم تر است و به شما امکان استفاده مجدد از آن را می دهد. اما علاوه بر این، راه را برای برخی از امکانات جدید باز می کند که کاملاً با چنین معماری سازگار است. به عنوان مثال، نظرسنجی و بارگذاری مقالات در فید همانطور که ظاهر می شوند. ما می توانیم به سادگی آخرین پست ها را در یک "ذخیره سازی" قرار دهیم فهرست مقالات، فهرست جداگانه ای از شناسه های جدید را در آن ذخیره کنید شناسه مقالات و کاربر را در مورد آن مطلع کنید. وقتی روی دکمه "نمایش انتشارات جدید" کلیک می کنیم، به سادگی شناسه های جدید را در ابتدای آرایه لیست فعلی مقالات وارد می کنیم و همه چیز تقریباً به شکل جادویی کار می کند.

لذت بردن بیشتر از دانلود

یخ روی کیک بازسازی مفهوم اسکلت است که روند دانلود محتوا در اینترنت کند را کمی کمتر منزجر کننده می کند. هیچ بحثی در مورد این موضوع وجود نداشت؛ مسیر ایده تا نمونه اولیه به معنای واقعی کلمه دو ساعت طول کشید. طراحی عملاً خودش را ترسیم کرد و ما به اجزای خود آموزش دادیم که بلوک‌های div ساده و به سختی سوسوزن را در حین انتظار برای داده ارائه دهند. از نظر ذهنی، این رویکرد برای بارگذاری در واقع میزان هورمون های استرس را در بدن کاربر کاهش می دهد. اسکلت شبیه به این است:

لاگ های توسعه دهنده جلویی Habr: بازسازی و بازتاب
هابرا بارگذاری

منعکس کننده

من شش ماه است که در هابره کار می کنم و دوستانم هنوز می پرسند: خوب، آنجا را چگونه دوست داری؟ خوب، راحت - بله. اما چیزی وجود دارد که این اثر را از بقیه متمایز می کند. من در تیم هایی کار کردم که نسبت به محصول خود کاملاً بی تفاوت بودند، نمی دانستند یا درک نمی کردند که کاربران آنها چه کسانی هستند. اما اینجا همه چیز متفاوت است. در اینجا شما نسبت به کاری که انجام می دهید احساس مسئولیت می کنید. در فرآیند توسعه یک ویژگی، تا حدی مالک آن می شوید، در تمام جلسات محصول مربوط به عملکرد خود شرکت می کنید، پیشنهاد می دهید و خودتان تصمیم می گیرید. ساختن محصولی که خودتان هر روز از آن استفاده می کنید بسیار جالب است، اما نوشتن کد برای افرادی که احتمالاً در آن بهتر از شما هستند، فقط یک احساس باورنکردنی است (بدون کنایه).

پس از انتشار همه این تغییرات، بازخورد مثبتی دریافت کردیم و بسیار بسیار زیبا بود. این الهام بخش است. متشکرم! بیشتر بنویس

اجازه دهید یادآوری کنم که پس از متغیرهای جهانی تصمیم گرفتیم معماری را تغییر دهیم و لایه پروکسی را به یک نمونه جداگانه اختصاص دهیم. معماری "دو گره" قبلاً در قالب آزمایش بتا عمومی منتشر شده است. اکنون هر کسی می‌تواند به آن روی بیاورد و به ما کمک کند تا Habr تلفن همراه را بهتر کنیم. برای امروز کافی است. خوشحال می شوم به تمام سوالات شما در نظرات پاسخ دهم.

منبع: www.habr.com

اضافه کردن نظر