جداول و لیست های بسیار بزرگ می توانند عملکرد سایت شما را به طور قابل توجهی کاهش دهند. مجازی سازی می تواند کمک کند!
react-window
کتابخانه ای است که به لیست های بزرگ اجازه می دهد تا به طور موثر ارائه شوند.
در اینجا نمونه ای از لیستی است که شامل 1000 ردیف است که با react-window
ارائه می شود. هر چه سریعتر اسکرول کنید.
چرا این مفید است؟
ممکن است زمان هایی وجود داشته باشد که شما نیاز به نمایش جدول یا لیست بزرگی داشته باشید که دارای ردیف های زیادی است. بارگیری تک تک موارد در چنین لیستی می تواند عملکرد قابل توجهی را تحت تأثیر قرار دهد.
مجازیسازی فهرست یا «پنجرهسازی» مفهومی است که تنها آنچه را که برای کاربر قابل مشاهده است ارائه میکند. تعداد عناصری که در ابتدا رندر می شوند، زیرمجموعه بسیار کوچکی از کل لیست است و زمانی که کاربر به پیمایش ادامه می دهد، «پنجره» محتوای قابل مشاهده حرکت می کند . این کار هم عملکرد رندر و هم عملکرد اسکرول لیست را بهبود می بخشد.
گرههای DOM که از «پنجره» خارج میشوند، بازیافت میشوند یا بلافاصله با اسکرول کردن کاربر در فهرست با عناصر جدیدتر جایگزین میشوند. با این کار تعداد تمام عناصر رندر شده مخصوص اندازه پنجره حفظ می شود.
پنجره واکنش
react-window
یک کتابخانه کوچک و شخص ثالث است که ایجاد لیست های مجازی در برنامه شما را آسان تر می کند. تعدادی API پایه را ارائه می دهد که می توانند برای انواع مختلف لیست ها و جداول استفاده شوند.
زمان استفاده از لیست های اندازه ثابت
اگر فهرستی طولانی و تک بعدی از اقلام با اندازه یکسان دارید، از مؤلفه FixedSizeList
استفاده کنید.
import React from 'react';
import { FixedSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const ListComponent = () => (
<FixedSizeList
height={500}
width={500}
itemSize={120}
itemCount={items.length}
>
{Row}
</FixedSizeList>
);
export default ListComponent;
- مؤلفه
FixedSizeList
height
،width
وitemSize
برای کنترل اندازه آیتم های موجود در لیست می پذیرد. - تابعی که ردیف ها را رندر می کند به عنوان فرزند به
FixedSizeList
ارسال می شود. جزئیات مربوط به یک مورد خاص را می توان با آرگومانindex
(items[index]
) در دسترس قرار داد. - یک پارامتر
style
نیز به روش رندر ردیف ارسال می شود که باید به عنصر ردیف متصل شود. آیتمهای فهرست کاملاً با مقادیر ارتفاع و عرض آنها که به صورت خطی تخصیص داده شده است، قرار میگیرند و پارامترstyle
مسئول این است.
مثال Glitch که قبلا در این مقاله نشان داده شد، نمونه ای از یک جزء FixedSizeList
را نشان می دهد.
زمان استفاده از لیست های با اندازه متغیر
از مؤلفه VariableSizeList
برای ارائه فهرستی از مواردی که اندازه های متفاوتی دارند استفاده کنید. این کامپوننت مانند یک لیست اندازه ثابت کار می کند، اما در عوض به جای یک مقدار خاص، یک تابع برای prop itemSize
انتظار دارد.
import React from 'react';
import { VariableSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const getItemSize = index => {
// return a size for items[index]
}
const ListComponent = () => (
<VariableSizeList
height={500}
width={500}
itemCount={items.length}
itemSize={getItemSize}
>
{Row}
</VariableSizeList>
);
export default ListComponent;
تعبیه زیر نمونه ای از این کامپوننت را نشان می دهد.
تابع اندازه آیتم که به prop itemSize
ارسال می شود، ارتفاع ردیف را در این مثال تصادفی می کند. با این حال، در یک برنامه واقعی، باید منطق واقعی وجود داشته باشد که اندازه هر آیتم را تعریف کند. در حالت ایده آل، این اندازه ها باید بر اساس داده ها محاسبه شوند یا از یک API به دست آیند.
شبکه ها
react-window
همچنین از مجازی سازی لیست ها یا شبکه های چند بعدی پشتیبانی می کند. در این زمینه، "پنجره" محتوای قابل مشاهده با حرکت کاربر به صورت افقی و عمودی تغییر می کند.
به طور مشابه، هر دو مؤلفه FixedSizeGrid
و VariableSizeGrid
را می توان بسته به اینکه آیا اندازه آیتم های لیست خاص می تواند متفاوت باشد، استفاده می شود.
- برای
FixedSizeGrid
، API تقریباً یکسان است، اما با این واقعیت که ارتفاع، عرض و تعداد آیتمها باید برای ستونها و سطرها نمایش داده شوند. - برای
VariableSizeGrid
، هم عرض ستون و هم ارتفاع سطر را می توان با انتقال توابع به جای مقادیر به props مربوطه تغییر داد.
برای مشاهده نمونه هایی از شبکه های مجازی سازی شده به مستندات نگاهی بیندازید.
بارگذاری تنبل در اسکرول
بسیاری از وبسایتها با صبر کردن برای بارگیری و ارائه آیتمها در یک لیست طولانی تا زمانی که کاربر به پایین پیمایش کند، عملکرد را بهبود میبخشند. این تکنیک که معمولاً به عنوان "بارگذاری بی نهایت" نامیده می شود، گره های DOM جدیدی را به لیست اضافه می کند، زیرا کاربر از آستانه مشخصی نزدیک به انتها عبور می کند. اگرچه این بهتر از بارگیری همه موارد در یک لیست به طور همزمان است، اما اگر کاربر از این تعداد ردیف گذشته باشد، همچنان DOM را با هزاران ورودی ردیف پر می کند. این می تواند منجر به یک اندازه DOM بسیار بزرگ شود که با کندتر کردن محاسبات سبک و جهش های DOM شروع به تأثیر بر عملکرد می کند.
نمودار زیر می تواند به خلاصه کردن این موضوع کمک کند:
بهترین رویکرد برای حل این مشکل، ادامه استفاده از کتابخانهای مانند react-window
برای حفظ یک "پنجره" کوچک از عناصر در یک صفحه است، اما همچنین بارگذاری تنبل ورودیهای جدیدتر هنگام حرکت کاربر به پایین است. یک بسته جداگانه، react-window-infinite-loader
، این کار را با react-window
امکان پذیر می کند.
قطعه کد زیر را در نظر بگیرید که نمونه ای از وضعیت را نشان می دهد که در یک مؤلفه App
والد مدیریت می شود.
import React, { Component } from 'react';
import ListComponent from './ListComponent';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: [], // instantiate initial list here
moreItemsLoading: false,
hasNextPage: true
};
this.loadMore = this.loadMore.bind(this);
}
loadMore() {
// method to fetch newer entries for the list
}
render() {
const { items, moreItemsLoading, hasNextPage } = this.state;
return (
<ListComponent
items={items}
moreItemsLoading={moreItemsLoading}
loadMore={this.loadMore}
hasNextPage={hasNextPage}
/>
);
}
}
export default App;
یک متد loadMore
به یک ListComponent
فرزند ارسال میشود که حاوی لیست لودر بینهایت است. این مهم است زیرا لودر بینهایت باید برای بارگیری موارد بیشتر، زمانی که کاربر از نقطه خاصی عبور کرده است، یک تماس برگشتی ایجاد کند.
در اینجا ListComponent
که لیست را رندر می کند، می تواند شبیه به این باشد:
import React from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from "react-window-infinite-loader";
const ListComponent = ({ items, moreItemsLoading, loadMore, hasNextPage }) => {
const Row = ({ index, style }) => (
{/* define the row component using items[index] */}
);
const itemCount = hasNextPage ? items.length + 1 : items.length;
return (
<InfiniteLoader
isItemLoaded={index => index < items.length}
itemCount={itemCount}
loadMoreItems={loadMore}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
height={500}
width={500}
itemCount={itemCount}
itemSize={120}
onItemsRendered={onItemsRendered}
ref={ref}
>
{Row}
</FixedSizeList>
)}
</InfiniteLoader>
)
};
export default ListComponent;
در اینجا، مؤلفه FixedSizeList
درون InfiniteLoader
پیچیده شده است. قطعات تخصیص داده شده به لودر عبارتند از:
-
isItemLoaded
: روشی که بررسی می کند آیا یک آیتم خاص بارگذاری شده است یا خیر -
itemCount
: تعداد موارد موجود در لیست (یا مورد انتظار) -
loadMoreItems
: Callback که وعده ای را برمی گرداند که به داده های اضافی لیست حل می شود
یک پروپ رندر برای برگرداندن تابعی استفاده می شود که جزء لیست از آن برای رندر کردن استفاده می کند. هر دو ویژگی onItemsRendered
و ref
ویژگی هایی هستند که باید به آنها منتقل شوند.
در زیر مثالی از نحوه کار بارگذاری بی نهایت با یک لیست مجازی ارائه شده است.
اسکرول کردن به پایین لیست ممکن است همین احساس را داشته باشد، اما اکنون درخواستی برای بازیابی 10 کاربر از یک API تصادفی کاربر هر بار که به انتهای لیست نزدیک می شوید، ارسال می شود. این همه در حالی انجام می شود که تنها یک "پنجره" از نتایج در یک زمان ارائه می شود.
با بررسی index
یک آیتم خاص، بسته به اینکه آیا درخواستی برای ورودی های جدیدتر انجام شده و آیتم هنوز در حال بارگیری است، می توان وضعیت بارگیری متفاوتی را برای یک آیتم نشان داد.
به عنوان مثال:
const Row = ({ index, style }) => {
const itemLoading = index === items.length;
if (itemLoading) {
// return loading state
} else {
// return item
}
};
Overscanning
از آنجایی که موارد موجود در یک لیست مجازی تنها زمانی تغییر میکنند که کاربر پیمایش کند، فضای خالی میتواند برای مدت کوتاهی چشمک بزند، زیرا ورودیهای جدیدتر نمایش داده میشوند. میتوانید هر یک از نمونههای قبلی را در این راهنما به سرعت پیمایش کنید تا متوجه این موضوع شوید.
برای بهبود تجربه کاربری لیست های مجازی شده، react-window
به شما امکان می دهد موارد را با ویژگی overscanCount
اسکن کنید. این به شما امکان میدهد تا در همه زمانها تعداد آیتمهای خارج از پنجره قابل مشاهده را تعریف کنید.
<FixedSizeList
//...
overscanCount={4}
>
{...}
</FixedSizeList>
overscanCount
برای هر دو مؤلفه FixedSizeList
و VariableSizeList
کار می کند و مقدار پیش فرض آن 1 است. بسته به بزرگی یک لیست و همچنین اندازه هر مورد، اسکن بیش از حد بیش از یک ورودی می تواند به جلوگیری از فلش محسوس فضای خالی کمک کند. اسکرول های کاربر با این حال، اسکن بیش از حد ورودیها میتواند بر عملکرد تأثیر منفی بگذارد. هدف اصلی استفاده از یک لیست مجازی، به حداقل رساندن تعداد ورودی ها به آنچه کاربر در هر لحظه می تواند ببیند است، بنابراین سعی کنید تعداد موارد بیش از حد اسکن شده را تا حد امکان کم نگه دارید.
برای FixedSizeGrid
و VariableSizeGrid
، از ویژگیهای overscanColumnsCount
و overscanRowsCount
برای کنترل تعداد ستونها و ردیفها برای اسکن بیشازحد استفاده کنید.
نتیجه گیری
اگر مطمئن نیستید که از کجا مجازی سازی لیست ها و جداول را در برنامه خود شروع کنید، این مراحل را دنبال کنید:
- عملکرد رندر و پیمایش را اندازه گیری کنید. این مقاله نشان میدهد که چگونه میتوان از FPS متر در Chrome DevTools برای بررسی میزان کارآمد بودن موارد در فهرست استفاده کرد.
- برای هر لیست طولانی یا شبکههایی که بر عملکرد تأثیر میگذارند،
react-window
اضافه کنید. - اگر ویژگیهای خاصی در
react-window
پشتیبانی نمیشوند، اگر خودتان نمیتوانید این قابلیت را اضافه کنید، ازreact-virtualized
استفاده کنید. - در صورت نیاز به بارگذاری تنبل آیتم ها در حین حرکت کاربر، لیست مجازی خود را با
react-window-infinite-loader
بپیچید. - از ویژگی
overscanCount
برای لیست های خود و از ویژگی هایoverscanColumnsCount
وoverscanRowsCount
برای شبکه های خود استفاده کنید تا از فلش محتوای خالی جلوگیری کنید. از اسکن بیش از حد ورودی ها خودداری کنید زیرا این کار بر عملکرد تأثیر منفی می گذارد.