עיבוד מראש של מסלולים באמצעות תגובה מהירה

אתם לא רוצים רינדור בצד השרת, אבל אתם עדיין רוצים לזרז את הביצועים של אתר React? כדאי לנסות את הרינדור מראש!

react-snap היא ספרייה של צד שלישי שמעבדת מראש דפים באתר שלכם לקובצי HTML סטטיים. הפעולה הזו יכולה לשפר את זמני First Paint באפליקציה.

הנה השוואה של אותה אפליקציה עם ובלי עיבוד מראש שנטען מראש בחיבור 3G ובמכשיר נייד:

השוואת טעינה זה לצד זה. הגרסה שנעשה בה שימוש בעיבוד מראש נטענת ב-4.2 שניות מהר יותר.

למה זה שימושי?

בעיית הביצועים העיקרית באפליקציות גדולות הכוללות דף יחיד היא שהמשתמש צריך להמתין עד שחבילות ה-JavaScript שמרכיבים את האתר יסיימו את ההורדה, לפני שיוכלו לראות תוכן אמיתי. ככל שהחבילות גדולות יותר, כך המשתמש יצטרך להמתין יותר זמן.

כדי לפתור את הבעיה, מפתחים רבים נוקטים בגישה של רינדור האפליקציה בשרת במקום להפעיל אותה רק בדפדפן. בכל מעבר של דף/נתיב, ה-HTML המלא נוצר בשרת ונשלח לדפדפן, וכך מפחית את זמני ה-FP, אבל עולה על זמן איטי יותר עד ל-First Byte.

עיבוד מראש הוא שיטה נפרדת שהיא פחות מורכבת מעיבוד באמצעות שרת, אבל היא גם מספקת דרך לשיפור זמני 'עיבוד ראשון' באפליקציה. דפדפן ללא GUI, או דפדפן ללא ממשק משתמש, משמשים ליצירת קובצי HTML סטטיים של כל מסלול במהלך זמן ה-build. לאחר מכן אפשר לשלוח את הקבצים האלה יחד עם חבילות ה-JavaScript שנחוצות לאפליקציה.

תגובה מהירה

react-snap משתמש ב-Puppeteer כדי ליצור קובצי HTML שעברו עיבוד מראש של מסלולים שונים באפליקציה שלכם. כדי להתחיל, התקינו אותה כתלויה בפיתוח:

npm install --save-dev react-snap

לאחר מכן צריך להוסיף סקריפט postbuild אל package.json:

"scripts": {
  //...
  "postbuild": "react-snap"
}

הפעולה הזו תריץ באופן אוטומטי את הפקודה react-snap בכל פעם build חדש של האפליקציות שנוצרו (npm build).

הדבר האחרון שצריך לעשות הוא לשנות את אופן אתחול האפליקציה. משנים את הקובץ src/index.js להגדרות הבאות:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

במקום להשתמש רק ב-ReactDOM.render כדי לעבד את רכיב התגובה הבסיסי (root) ישירות ל-DOM, המטרה היא לבדוק אם כבר קיימים צומתי צאצא כדי לקבוע אם תוכן HTML עובד מראש (או עובד בשרת). במקרה כזה, במקום זאת נשתמש ב-ReactDOM.hydrate כדי לצרף מאזיני אירועים ל-HTML שכבר נוצר, במקום ליצור אותו מחדש.

בניית האפליקציה תיצור עכשיו קובצי HTML סטטיים כמטענים ייעודיים (payloads) לכל מסלול שנסרק. כדי לראות איך נראה המטען הייעודי (Payload) של HTML, לוחצים על כתובת ה-URL של בקשת ה-HTML ואז על הכרטיסייה Previews בכלי הפיתוח ל-Chrome.

השוואה מסוג &#39;לפני ואחרי&#39;. הצילום שלאחר מכן מראה שהתוכן עבר רינדור.

Flash של תוכן ללא עיצוב

למרות ש-HTML סטטי מעובד באופן כמעט מיידי, הוא עדיין נשאר ללא עיצוב כברירת מחדל, דבר שעלול לגרום לבעיה בהצגת "הבהוב של תוכן ללא סגנון" (FOUC). זה בולט במיוחד אם משתמשים בספריית CSS-in-JS כדי ליצור סלקטורים, כי חבילת ה-JavaScript צריכה להסתיים לפני שאפשר יהיה להחיל סגנונות.

כדי למנוע זאת, ניתן להוסיף ישירות ל-<head> של מסמך ה-HTML את ה-CSS הקריטי, או את הכמות המינימלית של ה-CSS הדרושה לעיבוד הדף הראשוני. react-snap משתמש בספרייה אחרת של צד שלישי בחלק התחתון, minimalcss, כדי לחלץ CSS קריטי לנתיבים שונים. כדי להפעיל את האפשרות הזו, מציינים את הפרטים הבאים בקובץ package.json:

"reactSnap": {
  "inlineCss": true
}

בתצוגה המקדימה של התשובה בכלי הפיתוח ל-Chrome תוכלו לראות עכשיו את הדף המעוצב עם טקסט קריטי של CSS.

השוואה מסוג &#39;לפני ואחרי&#39;. הצילום שאחרי התמונה מראה שהתוכן עבר רינדור ומסוגנן באמצעות רכיב CSS קריטי מודגש.

סיכום

אם האפליקציה שלכם לא כוללת מסלולי רינדור בצד השרת, צריך להשתמש ב-react-snap כדי לעבד מראש HTML סטטי למשתמשים.

  1. מתקינים אותו כתלויות בפיתוח ומתחילים רק עם הגדרות ברירת המחדל.
  2. אפשר להשתמש באפשרות הניסיונית inlineCss כדי להטמיע CSS קריטי אם הוא מתאים לאתר שלכם.
  3. אם אתם משתמשים בפיצול קוד ברמת הרכיב בתוך מסלולים כלשהם, הקפידו לא לעבד מראש את מצב הטעינה למשתמשים שלכם. קובץ ה-README של react-snap מספק פרטים נוספים בנושא.