
سوپراپ سازمانی - سرویس فروش
ایران فاوا گسترش · گروه صنعتی ایرانخودرو
- React
- Vite
- Zustand
- SCSS
- PWA / Workbox
- React Compiler
- SSO
- Performance
- Accessibility
- Azure DevOps
سرویس فروش درون سوپراپ گروه ایرانخودرو: مرور طرحهای فروش فعال، ثبتنام در یک عرضه با قواعد فروش مستقیم، قرعهکشی یا فروش فوری، پرداخت، و پیگیری سفارش. یک بیلد واحد در سه جا اجرا میشود - درون WebView بومی سوپراپ، بهصورت PWA نصبشده روی صفحهی اصلی گوشی، و در یک تب معمولی مرورگر - و همین سطح است که بار روز عرضه را به دوش میکشد.
چالش
اپلیکیشن پرترافیک فروش خودرو برای فروش مستقیم، قرعهکشی و فروش فوری، به شکل یک PWA قابل نصب - بازسازیشده بر پایهی اندازهگیری، با ۶۹٪ کاهش حجم اولین بازدید و رساندن امتیاز دسترسپذیری Lighthouse به ۱۰۰.
تقاضا پخش نمیشود. فروش در دقیقهای اعلامشده باز میشود و تمام جمعیت واجد شرایط در همان چند ثانیه میرسند؛ تقریباً همه با موبایل و بسیاری با اتصالهایی که کند هستند، نه قطع. در این شکل از ترافیک، هر کیلوبایتِ اولین بازدید یک تصمیم ظرفیتی است، نه یک سلیقه. مشکل سختتر این بود که هیچکس نمیدانست اولین بازدید واقعاً چقدر هزینه دارد. عددی که ثبت شده بود حجم فایلها روی دیسک بود، و امتیاز کاراییای که همه نقل میکردند روی سرور توسعه گرفته شده بود - سروری که باندل نمیکند، کد را کوچک نمیکند و React را در حالت توسعه اجرا میکند. پس کار با اندازهگیری شروع شد، نه بهینهسازی؛ و اولین درخواست واقعی به یک نمونهی در حال اجرا نشان داد بزرگترین باندل بدون فشردهسازی و حتی بدون هدر Content-Encoding میرسد.
کاری که کردم
- بایتهای تحویلشده را اندازه گرفتم، نه بایتهای بیلدشده را. معلوم شد فشردهسازی برای همهچیز جز HTML خاموش است - پیشفرض سرور - و ۶۲٪ از حجم JavaScript فقط با یک تغییر پیکربندی برگشت، بدون دست زدن به کد اپلیکیشن.
- باندل را در سطح route تقسیم کردم، با یک chunk مشترکِ عمداً کوچک که فقط چیزی را نگه میدارد که همهی routeها لازم دارند، تا میان دیپلویها در کش مرورگر بماند. بقیه همراه همان routeی بارگذاری میشود که واقعاً از آن استفاده میکند - کتابخانههای انتخاب تاریخ و تقویم با تنها فرمی که لازمشان دارد میآیند، نه در صفحهی ورود.
- چهار وزن فونت TTF را با WOFF2 زیرمجموعهشدهای که یک مرحلهی بیلد تولید میکند جایگزین کردم: از ۴۷۶ به ۱۳۵ کیلوبایت، و چهار خانوادهی جدا به یک خانواده با چهار وزن واقعی تبدیل شد.
- دو ابزار ممیزی بدون وابستگی روی Chrome DevTools Protocol ساختم، چون شبکهی داخلی به رجیستری npm دسترسی نداشت و Lighthouse نصب نمیشد. این ابزارها بیلد production را اجرا میکنند و کنتراست واقعیِ رندرشده، اندازهی واقعی اهداف لمسی و نامهای دسترسپذیر را، در کنار FCP، LCP، CLS و حجم انتقال روی شبکه، اندازه میگیرند.
- ممیزی دسترسپذیری را با پیمایش واقعی اجرا کردم - کلیک به هر صفحه و سپس تأیید اینکه URL واقعاً به مقصد رسیده - چون اولین اجرای احرازشده چهار route محافظتشده را سالم گزارش کرده بود که هرگز به آنها نرسیده بود و در عمل چهار بار صفحهی مقصدِ ریدایرکت را ممیزی کرده بود.
- یک توکن رنگ را که دو کار انجام میداد دو تکه کردم. یک متغیر هم برای متن ثانویه و هم برای حاشیهها استفاده میشد، پس اصلاح کنتراست ۲٫۵۴:۱ آن روی سفید همهی حاشیههای سایت را تیره میکرد؛ یک توکن جدا برای placeholder سیزده کاربرد متنی را جابهجا کرد، بیآنکه به پانزده کاربرد حاشیه دست بزند.
- ارتفاع همهی اسکلتهای بارگذاری را از CSS کامپوننت واقعی استخراج کردم، نه با حدس، و کنار هر عدد توضیحی گذاشتم که از کدام کلاس آمده، تا تغییر طراحی بیصدا دوباره پرش صفحه را برنگرداند.
- برای Service Worker استراتژی کش جداگانه برای هر route نوشتم و صریح گفتم چه چیزی نباید کش شود: endpointهای احرازشده بهصراحت NetworkOnly تعریف شدند، نه اینکه فقط از قلم بیفتند، چون کش مرورگر میان همهی کسانی که از یک دستگاه استفاده میکنند مشترک است. کاتالوگ عمداً network-first و بدون timeout است - قیمت قدیمی باید وقتی نمایش داده شود که شبکه واقعاً قطع است، نه وقتی کند است.
- مدیریت خطای 401 را در هر چهار حالت اجرا یکسان کردم، در حالی که فقط یکی درست کار میکرد؛ یک حلقهی ریدایرکت SSO میان دامنهها را با تشخیص اینکه آیا نشست هرگز موفق بوده یا نه شکستم؛ و دریافت توکن را کلاً از React بیرون بردم، چون effect یک فرزند پیش از effectی اجرا میشد که توکن را ذخیره میکرد و اولین درخواست بدون احراز هویت ارسال میشد.
- نام bindingهای store را در ۲۷ فایل به قرارداد use* تغییر دادم تا React Compiler خواندن از store را فراخوانی خالص فرض نکند و پشت یک sentinel مموایز نکند - کاری که ترتیب hookها را به هم میزند و کامپوننت را در production از کار میاندازد.
- یک بهینهسازی preload فونت را اندازه گرفتم و بعد حذفش کردم. همان کاری را کرد که قرار بود - کشف فونت را از حدود ۱۶۰۰ به حدود ۲۰۵ میلیثانیه رساند - اما هفت اجرا برای هر حالت نشان داد FCP و LCP هر دو حدود ۴۰۰ میلیثانیه بدتر شدهاند: روی لینک موبایلِ محدودشده، پهنای باند گلوگاه است، نه ترتیب کشف. استدلال و اعداد در خود کد ماندند تا نفر بعدی همین مسیر را دوباره نرود.
ویژگیهای کلیدی
- حجم اولین بازدید از ۱۶۶۸ به ۵۲۵ کیلوبایت (−۶۹٪)؛ JavaScript اولیه از ۹۸۶ به ۳۷۰ کیلوبایت، CSS از ۲۰۶ به ۲۰٫۳ کیلوبایت، فونتها از ۴۷۶ به ۱۳۵ کیلوبایت.
- امتیاز دسترسپذیری Lighthouse از ۹۸ به ۱۰۰ و SEO از ۹۲ به ۱۰۰، با افزایش پوشش ممیزی از ۱۴ به ۲۲ وضعیت صفحهی احرازشده.
- جابهجایی تجمعی چیدمان (CLS) در صفحهی ورود از ۰٫۱۰ به ۰٫۰۰۱.
- PWA قابل نصب با استراتژیهای Workbox برای هر route، ثبت دستی Service Worker تا بیلد WebView آن را ثبت نکند، و بهروزرسانیای که منتظر کاربر میماند و وسط پر کردن فرم صفحه را تازه نمیکند.
- یک Content-Security-Policy سختگیر که روی ۱۱ route بدون هیچ تخلفی تأیید شد، بهعلاوهی پاکسازی HTML قراردادهای ارسالی از CMS با DOMPurify.
- صفر خطای ESLint، کاهش suppressionهای exhaustive-deps از ۲۸ به ۷، و اجرای اجباری Prettier با یک گیت pre-commit.
- بزرگترین فایل از ۲۰۵۰ به ۱۳۶۰ خط رسید، شش نسخهی تکراری یک مودال به یک کامپوننت تبدیل شد، و سیزده حالت خالیِ دستساز در نه فایل جای خود را به یک کامپوننت واحد دادند.
نتیجه
اندازهی سطح عرضه حالا با اندازهگیری تعیین میشود، نه تخمین؛ و دو ابزار ممیزی روی بیلد production اجرا میشوند، پس پسرفت در کنتراست، اندازهی اهداف لمسی یا حجم اولین بازدید را یک عدد میگیرد، نه بازبینی چشمی. مفیدترین نتیجه یک نتیجهی منفی بود - بهینهسازیای که کار کرد، اندازهگیری شد، ۴۰۰ میلیثانیه هزینه داشت، و همراه با شواهدش حذف شد.
صفحهها
برای دیدن هر صفحه در اندازهی کامل، رویش بزنید.