אף פעם אין צורך לשלוח למשתמשים יותר קוד מהנדרש, לכן חשוב לפצל את החבילות כדי לוודא שזה לא יקרה.
השיטה React.lazy
מאפשרת לפצל אפליקציית React בקלות ברמת הרכיב, באמצעות ייבוא דינמי.
import React, { lazy } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const DetailsComponent = () => (
<div>
<AvatarComponent />
</div>
)
למה המידע הזה מועיל?
בדרך כלל, אפליקציית React גדולה מורכבת מהרבה רכיבים, methods (methods) וספריות של צד שלישי. אם לא מנסים לטעון חלקים שונים של האפליקציה רק כשהם נדרשים, חבילת JavaScript גדולה אחת תישלח למשתמשים ברגע שהם יטענו את הדף הראשון. הדבר עלול להשפיע באופן משמעותי על ביצועי הדף.
הפונקציה React.lazy
מספקת דרך מובנית להפריד רכיבים באפליקציה לקטעי JavaScript נפרדים, בלי הרבה עבודה. לאחר מכן תוכלו לטפל בעומס המצבים כשאתם משלבים אותו עם הרכיב Suspense
.
מתח
הבעיה בשליחת מטען ייעודי (payload) גדול של JavaScript למשתמשים היא משך הזמן שיידרש להשלמת הטעינה של הדף, במיוחד במכשירים ובחיבורי רשת חלשים יותר. לכן, פיצול קוד וטעינה איטית הם כלים שימושיים מאוד.
עם זאת, תמיד יהיה עיכוב קל שהמשתמשים יצטרכו לחכות לו בזמן אחזור של רכיב שמפוצל לקוד דרך הרשת, לכן חשוב להציג מצב טעינה שימושי. כדי לפתור את הבעיה הזו, אפשר להשתמש ב-React.lazy
עם הרכיב Suspense
.
import React, { lazy, Suspense } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const renderLoader = () => <p>Loading</p>;
const DetailsComponent = () => (
<Suspense fallback={renderLoader()}>
<AvatarComponent />
</Suspense>
)
Suspense
מקבלת רכיב fallback
שמאפשר להציג כל רכיב React כסטטוס טעינה. הדוגמה הבאה ממחישה איך זה עובד.
הדמות המוצגת מונצחת רק כשלוחצים על הלחצן, ואז נשלחת בקשה לאחזור הקוד הנדרש ל-AvatarComponent
המושעה.
בינתיים, מוצג רכיב הטעינה החלופי.
כאן, הקוד שמרכיב את AvatarComponent
הוא קטן, ולכן סימן הציר של הטעינה מופיע רק לזמן קצר. טעינת רכיבים גדולים יותר עשויה להימשך הרבה יותר זמן, במיוחד בחיבור לרשת חלש.
כדי להמחיש בצורה טובה יותר איך זה עובד:
- כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה. לאחר מכן לוחצים על מסך מלא .
- מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools.
- לוחצים על הכרטיסייה רשתות.
- לוחצים על התפריט הנפתח Throttling (ויסות נתונים). כברירת מחדל, האפשרות שמוגדרת בתפריט היא No throttling (ללא ויסות נתונים). בוחרים באפשרות Fast 3G.
- לוחצים על הלחצן Click Me באפליקציה.
האינדיקטור לטעינה יוצג עכשיו למשך זמן ארוך יותר. שימו לב איך כל הקוד שמרכיבים את AvatarComponent
מאוחזר כמקטע נפרד.
השעיה של מספר רכיבים
תכונה נוספת של Suspense
היא שאפשר להשהות את הטעינה של כמה רכיבים, גם אם כולם נטענים באיטרציה.
לדוגמה:
import React, { lazy, Suspense } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));
const renderLoader = () => <p>Loading</p>;
const DetailsComponent = () => (
<Suspense fallback={renderLoader()}>
<AvatarComponent />
<InfoComponent />
<MoreInfoComponent />
</Suspense>
)
זוהי דרך שימושית מאוד לעכב את הרינדור של כמה רכיבים תוך הצגת מצב טעינה יחיד בלבד. בסיום אחזור כל הרכיבים, המשתמש יכול לראות את כולם מוצגים בו-זמנית.
כדי לראות את זה, אפשר להטמיע את הקוד הבא:
בלי זה, קל להיתקל בבעיה של טעינה מדורגת או של חלקים שונים בממשק המשתמש שנטענים אחד אחרי השני, כשלכל אחד מהם יש אינדיקטור טעינה משלו. כתוצאה מכך, חוויית המשתמש עשויה להיות קיצונית יותר.
טיפול בכשלים בטעינת הנתונים
Suspense
מאפשרת להציג מצב טעינה זמני בזמן שליחת בקשות לרשת. אבל מה קורה אם בקשות הרשת נכשלות מסיבה כלשהי? יכול להיות שאתם לא מחוברים לאינטרנט, או שאפליקציית האינטרנט מנסה לטעון באיטרציה כתובת URL עם גרסה מיושנת שלא זמינה יותר אחרי פריסה מחדש של השרת.
ל-React יש דפוס סטנדרטי לטיפול הולם בסוגים כאלה של כשלים בטעינה: באמצעות תחום שגיאה. כפי שמתואר במסמכי העזרה, כל רכיב React יכול לשמש כגבול שגיאה אם הוא מטמיע את אחת (או את שתיהן) משיטות מחזור החיים static getDerivedStateFromError()
או componentDidCatch()
.
כדי לזהות כשלים בטעינת נתונים בזמן אמת ולטפל בהם, אפשר לעטוף את הרכיב Suspense
ברכיבי הורה שמיועדים לשמש כגבול שגיאה. בתוך השיטה render()
של גבול השגיאה, אפשר להציג את הצאצאים כפי שהם אם אין שגיאה, או להציג הודעת שגיאה בהתאמה אישית אם משהו השתבש:
import React, { lazy, Suspense } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));
const renderLoader = () => <p>Loading</p>;
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {hasError: false};
}
static getDerivedStateFromError(error) {
return {hasError: true};
}
render() {
if (this.state.hasError) {
return <p>Loading failed! Please reload.</p>;
}
return this.props.children;
}
}
const DetailsComponent = () => (
<ErrorBoundary>
<Suspense fallback={renderLoader()}>
<AvatarComponent />
<InfoComponent />
<MoreInfoComponent />
</Suspense>
</ErrorBoundary>
)
סיכום
אם אתם לא בטוחים מאיפה כדאי להתחיל להחיל את חלוקת הקוד באפליקציית React, תוכלו לפעול לפי השלבים הבאים:
- מתחילים ברמת המסלול. נתיבים הם הדרך הפשוטה ביותר לזהות נקודות באפליקציה שאפשר לפצל. במסמכי העזרה של React מוסבר איך משתמשים ב-
Suspense
יחד עםreact-router
. - זיהוי רכיבים גדולים בדף באתר שמריצים רינדור רק בתגובה לאינטראקציות מסוימות של משתמשים (למשל לחיצה על לחצן). פיצול הרכיבים האלה יצמצם את מטענים ייעודיים (payloads) של JavaScript.
- כדאי לפצל כל תוכן אחר שלא מופיע במסך ולא קריטי למשתמש.