React Labs: על מה עבדנו - מרץ 2023
22 במרץ 2023 מאת Joseph Savona, Josh Story, Lauren Tan, Mengdi Chen, Samuel Susla, [Sathya Gunasekaran](TK_6](Mengdi Tank_6, חן](https://twitter.com/mengdi_en), סמואל סוסלה, Sathya Gunasekaran,](https://twitter.com/joshcstory),)(https://twitter.com/sebmarkbage), ו-TK_6__) קלארק](https://twitter.com/acdlite)
בפוסטים של React Labs אנחנו כותבים על פרויקטים במחקר ופיתוח פעילים. מאז העדכון הקודם הייתה התקדמות דומה, ורצינו לשתף מה למדנו.
React רכיבי שרת
React רכיבי שרת (או RSC) היא ארכיטקטורת אפליקציה חדשה שתוכננה על ידי צוות React.
סוג ראשון למחקר על נפרד RSC ב-הרצאת מבוא וב-RFC. בקצרה: אנחנו מציגים חדש של קומפונטות, רכיבי שרת, שרצות מראש וב-RFC. בקצרה: אנחנו מציגים חדש של קומפוננטות, רכיבי שרת, שרצות מראש ולא נכללות בחבילת של __K_T נתונים. props מ-Server Components ל-Client Components אינטראקטיביות בדפדפן.
RSC משלבת את המודל הפשוט של “בקשה/תגובה” מאפליקציות Multi-Page ממוקדות-שרת, עם האינטראקטיביות החלקה של ישומי עמוד יחיד ממוקדות-לקוח, כדי לקבל את הטוב משני העולמות.
העדכון האחרון מיזגנו את React Server Components RFC כדי לאשרר את ההצעה תמיכה. פתרנו נושאים פתוחים סביב React Server Module Conventions, והגענו להספיק עם השותפים לאמץ את הקונבנציית "use client". המסמכים.
השינוי הגדול ביותר הוא אימוץ async / await כדרך לטעינת נתונים מתוך רכיבי שרת. בנוסף, אנחנו מתכננים לתמוך בטעינת נתונים מצד הלקוח דרך Hook חדש בשם useורס שפ הבטחות. לתמוך באפליקציות במבנה של האפליקציה RSC למבנה.
אחרי שתחום טעינת להתייצב יחסית, אנחנו בוחנים גם את הכיוון ההפוך: שליחת נתונים מהלקוח לביצוע מוטציות במסד נתונים ולממש טפסים. אנחנו עושים זאת על ידי העברת פונקציות שרת פעולה דרך הגבול בין שרת ללקוח, כך שהלקוח יכול לקרוא להן וליהנות מ-RPC חלק. פעולות שרתות גם שיפור מתקדם לטפסים עוד לפני טעינת JavaScript.
React רכיבי שרת כבר נשלחה ב-Next.js App Router. זה מדגים אינטגרציה עמוקה של הנתב שמאמץ את RSC כ-פרימיטיבי מרכזי, אבל זו לא הדרך היחידה לבנות נתב/מסגרת תואמי-RSC. יש הפרדה ברורה יכולה בין שניתנות מהפרט והאפליקציה של RSC. React רכיבי שרת מיועדת להיות מפרט לקומפוננטות שעובדות בין frameworks תואמים.
באופן כללי אנחנו ממליצים להשתמש ב-framework קיים, אבל אם צריך לבנות מסגרת מותאמת אישית. בניית מסגרת תואם-RSC עדיין מורכבת יותר ממה שהיינו רוצים, בעיקר בגלל אינטגרציית מצרפת עמוקה שנדרשת. הדור הנוכחי של bundlers מצוין ללקוח, לא תוכנן עם תמיכה אבל גרף מודול ראשון בפיצול יחיד בין שרת ללקוח. אנחנו עובדים עם מפתחי באנדלר כדי להכניס פריטים מובנים ל-RSC.
נכסים
Suspense יכול להגדיר מה להצגה בזמן שהנתונים או הקוד של הקומפוננטות עדיין נטענים. כך משתמשים רואים יותר תוכן בהדרגה בזמן טעינת העמוד וגם בזמן ניווטים בנתב שטוענים עוד נתונים וקוד. אבל מנקודת מבט משתמש, טעינת נתונים ורינדור לא מספרים את כל הסיפור לגבי מוכנות התוכן. כברירת מחדל, דפדפנים טוענים גיליונות סגנונות, גופנים ותמונות בנפרד, מה שעלול לגרום לקפיצות UI ושינויי layout עוקבים.
אנחנו עובדים על טגרציה מלאה של Suspense עם מחזור הטעינה של גיליונות סגנונות, גופנים ותמונות, כך ש-React תביא אותם גם כדי לקבוע מתי התוכן באמת מוכן להראות. בלי לשנות את אופן הכתיבה של הקומפוננטות שלכם, העדכונים ירגישו קוהרנטיים ונעימים יותר. כאופטימיזציה, נספק גם דרך ידנית לבצע טעינה מראש כמו גופנים ממש מתוך קומפונטות.
היכולות האלה כרגע במימוש, ונשתף עוד בקרוב.
מטא נתונים של מסמך
עמודים ומסכים שונים באפליקציה לדרוש metadata שונה, כמו תגית <title>, תיאור, ותגיות <meta>תוספת. מבחינת תחזוקה, עדיף לשמור מידע קרוב לקומפונת React של אותו עמוד/מסך. אבל בפועל תגיות HTML אלו חייבות להיות בתוך <head> של המסמך, שבדרך כלל נרנדר מקומפוננטה בשורש האפליקציה.
היום פותרים את זה לרוב באחת משתי דרכים.
דרך אחת היא לרנדר קומפונט צד-שלישי מיוחדת שמעבירה <title>, <meta> ותגיות נוספות ל-<head>. זה עובד בדפדפנים מרכזיים, אבל יש לקוחות שלא מריצים JavaScript בצד אחד, כמו מנתחי גרפים פתוחים, זה לא גישה אוניברסלית.
דרך היא שנייה לרנדר את העמוד בשרת בשני שלבים: התוכן הקודם נאסף יחד עם כל התגיות הרלוונטיות, ואז מרנדרים <head> עם אותם תגיות, רק אז שולחים את הכול לדפדפן. הגישה הזו עובדת, אבל מונעת ניצול של React 18 Streaming Server Renderer, כי צריך לחכות לכל התוכן לפני שליחת <head>.
אנחנו מציעים לך תמיכה מובנית ברינדור <title>, <meta>, ותות מטא נתונים סטייל <link> בכל מקום בעץ הקומפוננטות, מהקופסה. זה יעבוד אותו בכל הסביבה: קוד צד לקוח מלא, SSR, ובעתיד גם RSC. נשתף פרטים נוספים בקרוב.
React אופטימיזציה מהדר
מאז העדכון הקודם עשינו איטרציה פעילה על העיצוב של React Forget, קומפיילר אופטימיזציה ל-React. דיברנו בעבר כ-”auto-memoizing compiler”, וזה נכון נכון. אבל העבודה עליו העמיקה אצלנו את ההבנה של מודל התכנות ב-React. דרך מדויקת יותר להבין את React שכח שהיא כקומפיילר תגובתיות אוטומטית.
המרכזי ב-React הוא מפתחים מגדירים UI כפונקציה של המצב הנוכחי. עובדים עם ערכי JavaScript רגילים - מספרים, מחרוזות, מערכים, אובייקטים - ומשתמשים באידיומים רגילים של JavaScript - if/else, for וכו’ - כדי לתאר את לוגי הקומפוננטה. המודל המנטלי הוא ש-React תרנדר מחדש כשמצב האפליקציה משתנה. אנחנו מאמינים שהמודל הפשוט הזה וההיצמדות לסמנטיקה של JavaScript הם עקרון חשוב ב-React.
האתגר הוא שלפעמים React מגיבה יותר מדי: היא מרנדרת מחדש יותר מדי. לדוגמה, ב-JavaScript אין דרך זולה להשוות אם שני אובייקטים/מערכים שקולים, יצירת אובייקט חדש בכל render יכול לגרום ל-React לבצע יותר עבודה מהנדרש. מפתחים נאלצים לבצע memoization מפורש כדי לא “להגיב יתר על המידה” לשינויים.
עניין שלנו ב-React שכח את כמות תגובתיות נכונה כברירת מחדל: שהאפליקציה תרנדר מחדש רק כשערכי state משתנים באופן משמעותי. מימוש חושב זה אומר memoization אוטומטי, אבל אנחנו ים מבחינת שהכ-reactivity מסביר טוב יותר את React ו-Forget. דרך להבין זאת: כיום React מרנדרת מחדש לפי שינוי בזהות אובייקט. עם תשכחו, React תרנדר מחדש לפי שינוי בערך הסמנטי, בלי לשלם עלות זמן ריצה של השוואות עמוקות.
מבחינת התקדמות קונקרטית, מאז העדכון האחרון עשינו איטרציה להתבצע על עיצוב הקומפיילר כדי ליישר אותו עם הגישה הזו ולשלב משוב משימוש פנימי. אחרי refactors גדולים בסוף השנה, התחלנו להשתמש בקומפיילר בפרודקשן באזורים מוגבלים ב-Meta. אנחנו מתכננים לפתוח אותו לקוד פתוח אחרי שנוכיח אותו בפרודקשן.
בנוסף, הרבה אנשים ביקשו להבין יותר איך הקומפיילר עובד. נשמח לשתף הרבה יותר פרטים אחרי שנוכיח ונפתח אותו. בינתיים אפשר לשתף כמה נקודות:
ליבת הקומפיילר כמעט מנותקת לגמרי מ-Babel, ו-API הליבה הוא בקירוב AST נכנס, AST יוצא (תוך שמירה על מיקומי מקור). מתחת למכסה המנוע אנחנו משתמשים בייצוג קוד מותאם וב-pipeline כדי לבצע ניתוח סמנטי ברמה נמוכה. עם זאת, הממשק הציבורי המרכזי לקומפיילר יהיה דרך בבל ותוספי לבנות נוספים. לצורכי בדיקות לנו כרגע תוסף בבל דק מאוד שקורא לקומפיילר, מייצר גרסה חדשה לכל פונקציה ומחליף אותה.
ה-refactor בימינו נמשך לנו להתמקד בשיפור מודל הקומפילציה המרכזית כדי לוודא שהוא מתמודד עם מורכבות כמו תנאים, לולאות, השמה מחדש ומוטציות. אבל ל-JavaScript יש רבות לבטא כל אחת מהיכולות האלה: if/else, ternaries, for, for-in, for-of וכו’. מלאה בכל השפה מראש הייתה מעכבת את התאימות של מודל הליבה. במקום זה התחלנו בת-קבוצה קטנה אך מייצגת: let/const, if/else, לולאות for, אובייקטים, מערכים, פרימיטיבים, קריאות פונקציה ועוד כמה אפשר. ככל שהביטחון במודל גדל והפשטנו את ההפשטות הפנימיות, הרחבנו את תת-הקבוצה הנתמכת. אנחנו גם מסמנים בפורש תחביר שלא נתמך עדיין, מדווחים אבחון ומדלגים על קומפילציה לקלט לא נתמך. יש לנו כלים להריץ את הקומפיילר על בסיסי הקוד של Meta ולזהות אפשריות חסרות הן הנפוצות ביותר כדי לתעדף בהמשך. נמשיך להרחיב בהדרגה עד תמיכה בשפה כולה.
כדי להפוך JavaScript רגיל בקומפוננטות React ל-reactive צריך להגיע להבנה סמנטית עמוקה, כדי להבין בדיוק מה הקוד עושה. בגישה הזו הזו בונים תגובתיות בתוך JavaScript שמאפשרת כתי מוצר מערכת בת בכל רמת מורכבות עם כל העוצמה של השפה, יכולה להיות מוגבלת לשפה ייעודית צרה.
עיבוד מחוץ למסך
רינדור מחוץ למסך היא יכולה מתוכננת ב-React לרינדור מסכים ברקע ללא עלות ביצועים נוספים. אפשר לחשוב עליה כגרסה של content-visibility ב-CSS שעובדת לא רק על אלמנטים ב-DOM אלא גם על קומפונטות React. מבחן מצאנו מגוון שימושים:
- נתת יכול לבצע prerender למסכים ברקע כך שכשה משתמש מנווט הם זמינים מיד.
- קומפוננטת החלפת טאבים יכולה לשמור את המצב של טאבים מוסתרים, כך שאפשר לעבור ביניהם בלי לאבד התקדמות.
- קומפוננטה רשימה וירטואלית יכולה לבצע prerender לשורות נוספות מעל ומתחת לאזור הגלוי.
- בזמן פתיחת מודאל/פופup אפשר להעביר את שאר האפליקציה מצב “רקע” כך שאירועים ועדכונים כבויים לכל מה שמחוץ למודל.
רוב מפתחי React לא יעבדו למעשה עם APIs של מחוץ למסך. במקום זה, עיבוד מחוץ למסך תשולב בראוטרים ובספריות UI, ומי שמשתמש בספריות האלה ירווחת בלי עבודה נוספת.
הרעיון הוא צריך לרנדר כל עץ React ב-offscreen בלי לשנות את אופן כתיבת הקומפוננטות. כשקומפוננת מרונדרת ב-offscreen היא לא באמת מתבצעת mount עד שהיא הופכת לגלויה, אז האפקט שלה לא מופעלים. לדוגמה, אם קומפונת משתמשת ב-useEffect כדי לרשום אנליטיקה כשהיא הופיעה לראשונה, עיבוד מוקדם לא יפגע בדיוק האנליטיקה. הם עושים ביטול. תכונה מרכזית היא יכולה להחליף נראות בלי לאבד state.
מאז העדכון האחרון בדקנו גרסה ניסיונית פנימית של עיבוד מראש באפליקציות React Native ב-Android וב-iOS עם ביצועים חיוביות. שיפרנו גם את האינטגרציה עם Suspense: השהיה בתוך עץ מחוץ למסך לא תפעיל Suspense fallbacks. העבודה שנותרה היא לסיים את ה-primitives שנחשפים למפתחי ספריות. אנחנו מצפים לפרסם RFC בהמשך השנה יחד עם API ניסיוני לבדיקות ומשוב.
מעקב אחר מעבר
Transition Tracing API יכול לזהות מתי React Transitions נעשים איטיים ולחקור למה. אחרי העדכון האחרון השלמנו את העיצוב הראשוני ופרסמנו [RFC](useTransition היכולות הבסיסיות גם מומשו. כרגע הפרויקט בהשהיה זמנית. נשמח למשוב על ה-RFC ומצפים לחזור לפיתוח כדי לספק כלי מדידה ביצועים טובים יותר ל-React. נתב](/learn/start-a-new-react-project#nextjs-app-router).
בנוסף לעדכון הזה, הצוות שלנו התארח לאחרונה בפודקאסטים ושידורים חיים של הקהילה כדי לדבר על העבודה שלנו ולענות על שאלות.
- Dan Abramov ו-Joe Savona התראיינו אצל Kent C. Dodds ביוטיוב, ושם דיברו על חששות סביב React Server Components.
- דן אברמוב ו-Joe Savona היו אורחים ב-JSParty podcast ושיתפו מחשבות על עתיד React.
תודה ל-אנדרו קלארק, דן אברמוב, דייב מקייב, לונה ווי, מאט קרול, שון קיגן, [סבסטיאן זילברסטרמן]_____7K(https://twitter.com/mattcarrollcode),] אלפרט](https://twitter.com/sophiebits) על סקירת הפוסט הזה.
תודה שקראתם, נתראה בעדכון הבא!