رندر کردن دیفها؛ چطور هر تغییری رو فوری نشون بدیم
خلاصهٔ کاملتر
تیم Pierre توی این یادداشت تعریف میکنه چطور مشکل رندرکردن دیفهای بزرگ رو حل کردن. وقتی یه PR رو باز میکنی، برای تغییرهای کوچیک و متوسط همهچیز خوب کار میکنه؛ ولی وقتی چیز بزرگی باز میکنی (مثلاً پیادهسازی و تست و فیکسچرِ تولیدشده با ایجنت، یا برنچی که فایلهای زیادی رو دست زده)، سطح بازبینی شروع به افت میکنه — شاید فقط یه فایل رو همزمان نشون بده یا ناوبری کند بشه. بهگفتهی آنها رندرِ دیف مهمه ولی برای بیشتر ابزارها خودِ محصول نیست؛ محصول چیزیه که دور کد اتفاق میافته (گردشکار بازبینی، اتوماسیون، خروجی ایجنت، نتایج CI). برای همین حدود شش ماه پیش محصولی به اسم Diffs منتشر کردن و حالا کامپوننتی به اسم CodeView ساختن با یه هدفِ عمداً ناممکن: «باید بتونی هر دیفی رو فقط رندر کنی» — در هر مقیاسی، تقریباً فوری (که توی diffshub.com قابل امتحانه، کافیه github رو با diffshub عوض کنی).
مسئله سه دستهست: رندر (پیچیدگی DOM سریع زیاد میشه)، پردازش (هر عملیات فایل ضربدر تعداد فایلها میشه) و حافظه (فایلهای بزرگ به سقف حافظهی مرورگر فشار میارن). پایهی کار virtualization (پنجرهسازی) است؛ یعنی فقط بخش نزدیک viewport رندر میشه. مشکل بزرگش «خالیشدن» (blanking) است: چون مرورگر اسکرول رو جدا از اجرای جاوااسکریپت مدیریت میکنه، موقع اسکرول سریع، جاوااسکریپت عقب میمونه و کاربر وارد فضای خالی میشه.
راهحل خلاقانهی آنها «تکنیک Inverse Sticky» است. بهجای اینکه بالای محتوای رندرشده رو به بالای viewport بچسبونن، لبهی پایینِ ناحیهی رندرشده رو به پایین viewport میچسبونن (و موقع اسکرول به بالا، لبهی بالا رو به بالا). این کار اسکرول بومی رو حفظ میکنه و اگه جاوااسکریپت عقب بمونه، ناحیهی رندرشده بهجای اینکه از صفحه بره بیرون و فضای خالی نشون بده، به یه لبه میچسبه — پس خالیشدن عملاً ناممکن میشه (هرچند سافاری زیر اسکرول خیلی تهاجمی هنوز میتونه خرابش کنه).
برای چیدمان مقیاسپذیر هم از تخمینهای ارزون استفاده میکنن (فایل تقریباً برابر lineHeight × تعداد خط)، و برای هانکهای با صدها هزار خط، یه سیستم checkpointِ «موقعیت به خط» با جستجوی دودویی اضافه کردن تا نقطهی شروع نزدیکتری پیدا کنن. scroll anchoring رو هم خودشون مدیریت میکنن (با overflow-anchor: none) تا موقع تغییر چیدمان، نمای کاربر نپره. در نهایت سراغ حافظه رفتن: رشتههای پارسشده میتونن کل رشتهی غولپیکر اصلی رو زنده نگه دارن، پس با کپیکردن (جداکردن) عمدیِ خطها، حافظه آزاد میشه؛ بهعلاوهی pool کردن المنتهای DOM برای کاهش زبالهروبی و بهاشتراکگذاری state تنظیمات. نتیجهی این کارها روی دیف غولپیکر هستهی لینوکس (نسخهی ۶ به ۷): مصرف حافظه از ۲.۴ گیگابایت به حدود ۱.۱۵ گیگابایت رسید و زمان پارس حدود ۸۰٪ کم شد.
نکات کلیدی:
- CodeView برای رندرِ هر دیفی در هر مقیاسی و تقریباً فوری ساخته شده
- سه مسئله: پیچیدگی رندر DOM، پردازش ضربشده per-file و فشار حافظه
- تکنیک Inverse Sticky اسکرول بومی رو حفظ میکنه و جلوی خالیشدن صفحه رو میگیره
- با جداکردن رشتههای پارسشده و pool کردن المنتها، حافظهی دیف لینوکس از ۲.۴ به ۱.۱۵ گیگابایت رسید




