عنوان:

‫بررسی کامپوننت Virtualize در Blazor: بهینه‌سازی رندر لیست‌های بزرگ


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۲۸ ۰۸:۳۵
آدرس: www.dntips.ir
مقدمه
در توسعه برنامه‌های وب با فریم‌ورک Blazor، یکی از چالش‌های رایج، مدیریت و رندر لیست‌های بزرگ داده است. نمایش صدها یا هزاران آیتم در یک صفحه می‌تواند عملکرد برنامه را به شدت تحت تأثیر قرار دهد، به‌ویژه در Blazor WebAssembly که منابع محاسباتی محدودتری در مرورگر دارد. برای حل این مشکل، Blazor کامپوننت <Virtualize> را ارائه می‌دهد که از تکنیک مجازی‌سازی (Virtualization) برای بهینه‌سازی رندر لیست‌ها استفاده می‌کند. این مقاله به بررسی دقیق کامپوننت <Virtualize>، نحوه کارکرد آن، و بهترین روش‌های استفاده از آن در برنامه‌های .NET می‌پردازد. همچنین، با تمرکز بر توسعه‌دهندگان .NET، جزئیات فنی و مثال‌های کاربردی ارائه می‌شود تا درک عمیقی از این ابزار قدرتمند ایجاد شود.

مجازی‌سازی چیست و چرا مهم است؟
مجازی‌سازی در رابط کاربری به تکنیکی اشاره دارد که در آن تنها بخشی از داده‌ها که در دید کاربر (Viewport) قرار دارد، رندر می‌شود. به جای رندر کردن تمام آیتم‌های یک لیست بزرگ، مجازی‌سازی فقط آیتم‌های قابل‌مشاهده را در DOM (Document Object Model) قرار می‌دهد و با اسکرول کاربر، آیتم‌های جدید را به‌صورت پویا بارگذاری و آیتم‌های خارج از دید را حذف می‌کند. این روش چندین مزیت کلیدی دارد:
  • بهبود عملکرد: کاهش تعداد عناصر DOM باعث کاهش مصرف حافظه و افزایش سرعت رندر می‌شود.
  • کاهش تأخیر: بارگذاری تدریجی داده‌ها از تأخیرهای طولانی در رندر اولیه جلوگیری می‌کند.
  • پشتیبانی از داده‌های بزرگ: امکان نمایش لیست‌های بسیار بزرگ (حتی با میلیون‌ها آیتم) بدون افت عملکرد فراهم می‌شود.

در Blazor، کامپوننت <Virtualize> این تکنیک را به‌صورت داخلی پیاده‌سازی می‌کند و به توسعه‌دهندگان اجازه می‌دهد بدون نیاز به مدیریت دستی DOM، از مزایای مجازی‌سازی بهره‌مند شوند.

معرفی کامپوننت Virtualize
کامپوننت <Virtualize> در Blazor به‌طور خاص برای مدیریت لیست‌های بزرگ طراحی شده است. این کامپوننت به‌جای رندر کردن تمام آیتم‌های یک مجموعه داده، تنها زیرمجموعه‌ای از آیتم‌ها را که در محدوده دید کاربر قرار دارند، رندر می‌کند. <Virtualize> از یک مکانیزم اسکرول (Scroll-Based Rendering) استفاده می‌کند که با حرکت کاربر در لیست، آیتم‌های جدید را به‌صورت پویا بارگذاری می‌کند.
برای استفاده از <Virtualize>، باید آن را در فایل Razor خود اضافه کنید و یک منبع داده (مانند IEnumerable<T> یا IQueryable<T>) به آن متصل کنید. کد زیر یک مثال ساده از استفاده از <Virtualize> را نشان می‌دهد:
<Virtualize Items="items" Context="item">
    <div>@item.Name</div>
</Virtualize>

@code {
    private List<ItemModel> items = Enumerable.Range(1, 1000)
        .Select(i => new ItemModel { Id = i, Name = $"Item {i}" })
        .ToList();
}
در این مثال، items یک لیست با 1000 آیتم است، اما <Virtualize> تنها تعداد کمی از آن‌ها (متناسب با اندازه viewport) را در هر لحظه رندر می‌کند. ویژگی Context امکان دسترسی به هر آیتم در قالب را فراهم می‌کند، مشابه متغیر حلقه در @foreach.

ویژگی‌ها و پارامترهای کلیدی Virtualize
کامپوننت <Virtualize> چندین پارامتر و ویژگی ارائه می‌دهد که به توسعه‌دهندگان امکان کنترل دقیق‌تر بر رفتار مجازی‌سازی را می‌دهد:
  1. Items: منبع داده اصلی که می‌تواند یک IEnumerable<T> یا ICollection<T> باشد. این پارامتر برای لیست‌های ثابت مناسب است.
  2. ItemsProvider: برای بارگذاری پویا و تدریجی داده‌ها (مانند داده‌های دریافتی از یک API یا پایگاه داده) استفاده می‌شود. این پارامتر یک delegate از نوع ItemsProviderDelegate<T> را می‌پذیرد که داده‌ها را به‌صورت ناهمگام (Asynchronous) بارگذاری می‌کند.
  3. ItemSize: اندازه تقریبی هر آیتم در لیست (به پیکسل) را مشخص می‌کند. این پارامتر به Blazor کمک می‌کند تا تعداد آیتم‌های قابل‌مشاهده را محاسبه کند. مقدار پیش‌فرض آن 50 پیکسل است.
  4. OverscanCount: تعداد آیتم‌های اضافی که قبل و بعد از محدوده قابل‌مشاهده رندر می‌شوند. این ویژگی به بهبود تجربه اسکرول روان کمک می‌کند. مقدار پیش‌فرض آن 3 است.
  5. ItemContent: یک قالب رندر (Render Fragment) که مشخص می‌کند هر آیتم چگونه نمایش داده شود.
  6. Placeholder: یک قالب برای نمایش آیتم‌های در حال بارگذاری (Loading Placeholder) زمانی که داده‌ها هنوز در دسترس نیستند.

استفاده از ItemsProvider برای بارگذاری پویا
یکی از قابلیت‌های قدرتمند <Virtualize>، امکان بارگذاری داده‌ها به‌صورت تدریجی با استفاده از ItemsProvider است. این ویژگی برای سناریوهایی که داده‌ها از یک سرور یا پایگاه داده دریافت می‌شوند، بسیار مفید است. مثال زیر نحوه استفاده از ItemsProvider را نشان می‌دهد:
<Virtualize ItemsProvider="LoadItems" Context="item">
    <ItemContent>
        <div>@item.Name</div>
    </ItemContent>
    <Placeholder>
        <div>Loading...</div>
    </Placeholder>
</Virtualize>

@code {
    private async ValueTask<ItemsProviderResult<ItemModel>> LoadItems(ItemsProviderRequest request)
    {
        // شبیه‌سازی دریافت داده از API
        var items = await Task.Delay(500).ContinueWith(_ =>
            Enumerable.Range(request.StartIndex, request.Count)
                .Select(i => new ItemModel { Id = i + 1, Name = $"Item {i + 1}" })
                .ToList());

        return new ItemsProviderResult<ItemModel>(items, totalItemCount: 10000);
    }
}
در این مثال، LoadItems داده‌ها را به‌صورت ناهمگام بارگذاری می‌کند. پارامتر request اطلاعاتی مانند StartIndex (نقطه شروع آیتم‌ها) و Count (تعداد آیتم‌های درخواستی) را فراهم می‌کند. ItemsProviderResult شامل لیست آیتم‌ها و تعداد کل آیتم‌های موجود (totalItemCount) است که برای محاسبه اسکرول‌بار ضروری است.

ترکیب Virtualize با @key
همان‌طور که در مقاله قبلی بحث شد، استفاده از دایرکتیو @key برای حفظ هویت منحصربه‌فرد کامپوننت‌ها در لیست‌های پویا ضروری است. این موضوع در <Virtualize> نیز صدق می‌کند، به‌ویژه زمانی که آیتم‌ها دارای حالت داخلی (مانند فیلدهای ورودی یا چک‌باکس) هستند. برای اطمینان از رفتار صحیح، می‌توانید @key را در قالب ItemContent اضافه کنید:
<Virtualize Items="items" Context="item">
    <ItemContent>
        <div @key="item.Id">
            <input type="checkbox" @bind="item.IsSelected" /> @item.Name
        </div>
    </ItemContent>
</Virtualize>
در این کد، @key="item.Id" تضمین می‌کند که حالت چک‌باکس‌ها با آیتم‌های درست حفظ شود، حتی زمانی که کاربر در لیست اسکرول می‌کند و آیتم‌ها به‌صورت پویا رندر می‌شوند.

بهترین روش‌ها و نکات پیشرفته
برای استفاده بهینه از <Virtualize>، رعایت چند نکته ضروری است:
  1. انتخاب ItemSize مناسب: تنظیم دقیق ItemSize متناسب با ارتفاع واقعی آیتم‌ها می‌تواند از مشکلات اسکرول مانند پرش (Jumping) جلوگیری کند. اگر آیتم‌ها ارتفاع متغیری دارند، از CSS برای اطمینان از ارتفاع ثابت استفاده کنید.
  2. بهینه‌سازی ItemsProvider: هنگام استفاده از ItemsProvider، درخواست‌های سرور را با کش کردن (Caching) یا محدود کردن تعداد درخواست‌ها (Debouncing) بهینه کنید تا از بار اضافی روی سرور جلوگیری شود.
  3. مدیریت Placeholder: استفاده از یک Placeholder مناسب، تجربه کاربری بهتری در زمان بارگذاری داده‌ها فراهم می‌کند. اطمینان حاصل کنید که Placeholder از نظر بصری با آیتم‌های واقعی سازگار است.
  4. آزمایش در سناریوهای واقعی: عملکرد <Virtualize> را با داده‌های واقعی و در مرورگرهای مختلف تست کنید، زیرا رفتار اسکرول ممکن است بسته به مرورگر متفاوت باشد.
  5. ترکیب با Lazy Loading: برای داده‌های بسیار بزرگ، <Virtualize> را با تکنیک‌های Lazy Loading در سمت سرور ترکیب کنید تا فقط داده‌های موردنیاز بارگذاری شوند.

محدودیت‌ها و ملاحظات
با وجود مزایای زیاد، <Virtualize> محدودیت‌هایی نیز دارد:
  • عدم پشتیبانی از لیست‌های دوبعدی: در حال حاضر، <Virtualize> تنها برای لیست‌های خطی (عمودی یا افقی) طراحی شده و از گریدهای دوبعدی پشتیبانی نمی‌کند.
  • پیچیدگی در سناریوهای پویا: اگر ارتفاع آیتم‌ها به‌صورت پویا تغییر کند، ممکن است نیاز به تنظیمات اضافی در CSS یا JavaScript باشد.
  • عملکرد در Blazor Server: در Blazor Server، تأخیر شبکه می‌تواند بر تجربه اسکرول تأثیر بگذارد، بنابراین بهینه‌سازی ارتباط SignalR ضروری است.

نتیجه‌گیری
کامپوننت <Virtualize> در Blazor یک ابزار قدرتمند برای بهینه‌سازی رندر لیست‌های بزرگ است که با کاهش تعداد عناصر DOM و بارگذاری پویا داده‌ها، عملکرد برنامه‌های وب را به‌طور قابل‌توجهی بهبود می‌بخشد. با استفاده از ویژگی‌هایی مانند ItemsProvider و ترکیب آن با دایرکتیو @key، توسعه‌دهندگان .NET می‌توانند برنامه‌هایی مقیاس‌پذیر و کاربرپسند بسازند. رعایت بهترین روش‌ها، مانند تنظیم دقیق ItemSize و بهینه‌سازی درخواست‌های سرور، به حداکثر رساندن کارایی این کامپوننت کمک می‌کند. با درک عمیق‌تر از <Virtualize> و کاربردهای آن، توسعه‌دهندگان می‌توانند چالش‌های مرتبط با داده‌های بزرگ را به‌طور مؤثری مدیریت کنند و تجربه کاربری بهتری ارائه دهند.


مشاهده مطلب اصلی