[ הרצאה · DOTB Meetup #4 · AI Era ]

אביתר אלוש על מסע של פרויקט אופן-סורס שכבש את הרשת

וידאו · 15 ביולי 2025 · אביתר אלוש

פרויקט אופן סורס לא נשבר רק בקוד. אביתר אלוש מראה איך חיפוש ב-React האט אלפי אימוג'ים, ולמה דווקא CSS בלי JavaScript החזיר את המהירות בפרודקשן.

צפו בהרצאה
תמונה ממוזערת של ההרצאה: אביתר אלוש על מסע של פרויקט אופן-סורס שכבש את הרשת
[ כתבת ההרצאה ]

הסיפור שמאחורי ההרצאה

[ תקציר מהיר ]
  • פרויקט אופן סורס פופולרי חושף בעיות שלא רואים לפני שמגיעים למשתמשים אמיתיים ובחומרה חלשה.
  • ב-Emoji Picker React, סינון של יותר מ-1,800 אימוג'ים דרש עבודה יקרה מול ה-DOM בכל הקלדה.
  • אביתר החליף את עדכון ה-state הגלובלי בחיפוש מבוסס CSS selectors, בלי להסיר וליצור מחדש אלמנטים.
  • הדמו אינו מתכון לכל מסך מוצר, אלא תזכורת לבדוק את ההנחות ואת ה-tradeoffs לפני שפוסלים כיוון.

פרויקט אופן סורס לא נהיה מעניין כשהוא מקבל כוכבים. הוא נהיה מעניין כשהוא מקבל משתמשים, תקלות, ובקשה לשמור על אותה חוויה גם במחשב שלא סלחני כלפי כל הקלדה. אביתר אלוש מספר על Emoji Picker React, ספרייה שמוצגת ביותר מ-63 אלף אפליקציות ב-GitHub ולדבריו יורדת מאות אלפי פעמים בשבוע ב-NPM. בסקייל כזה, חיפוש שנראה סביר בדמו התחיל להרגיש איטי. לא בגלל אלגוריתם מתוחכם במיוחד. בגלל עבודה קטנה שחוזרת על עצמה יותר מדי פעמים מול ה-DOM.

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

כשסקייל הופך חיפוש פשוט לבעיה

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

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

הניסיון הראשון שלו לא משנה את המהות: במקום להסיר אלמנטים, הוא חושב על שינוי class לכל אימוג'י, בסגנון jQuery. אבל גם שינוי כזה הוא שינוי DOM שמבוצע שוב ושוב. הבעיה אינה רק האם האלמנט נמחק. הבעיה היא לגעת באלפי אלמנטים בכל חיפוש. מכאן מגיע הניסוי: אם האימוג'ים כבר קיימים על המסך, אולי לא צריך לנהל אותם דרך רינדור חדש בכלל.

חיפוש CSS בלי לסנכרן state לכל הרשימה

אביתר מפריד בין שני דברים: ה-input צריך לדעת מה הוקלד, אבל האימוג'ים לא חייבים לקבל את הערך דרך state גלובלי. קומפוננטת החיפוש מחזיקה value מקומי. כשהוא ריק, אין מה לעשות. כשהוא מלא, היא מזריקה style tag שמכיל selectors שנבנים מהערך. כך עדכון React נשאר קרוב ל-input, והסינון עצמו נופל על מנגנון ה-CSS של הדפדפן.

החלק המכריע כבר נמצא ב-DOM: לכל אימוג'י יש ARIA label, למשל תיאור שמכיל את המילה king, winking או cooking. קודם מסתירים את כל האימוג'ים עם display: none. אחר כך משתמשים ב-attribute selector עם התאמה חלקית כדי להציג רק אלמנט שה-ARIA label שלו מכיל את מונח החיפוש. אין כאן מנוע חיפוש כללי, ואין הבטחה שזה נכון לכל שפה או לכל data set. יש ניצול של metadata שכבר הוגדר לצורכי נגישות.

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

גם קטגוריות ותוצאות אפשר לנהל ב-CSS

חיפוש שמסתיר אימוג'ים חושף מיד בעיה אחרת: קטגוריות ריקות. בגרסת הפרודקשן שלהן אין מקום. האתגר הוא שהקטגוריה עצמה לא מחזיקה ARIA label של האימוג'י. כאן אביתר משתמש ב-:has() וב-:not(): קטגוריה שאין בתוכה אימוג'י שמותאם לערך החיפוש מוסתרת. הוא אומר במפורש שזה לא בהכרח שימוש CSS יעיל, וזו בדיוק ההערה שצריך לזכור כשמעתיקים רעיון מהרצאה.

הוא ממשיך לספירת התוצאות. CSS counters מאתחלים מונה על הרשימה, מגדילים אותו על כל התאמה, ואז pseudo-element מציג את המספר. יש גם מלכודת קטנה: before נוצר לפני הספירה, ולכן רואה אפס; after נוצר אחרי האלמנטים ויכול להציג את המספר. כדי שהמונה יופיע בתחילת הרשימה ולא אחריה, הרשימה הופכת ל-flex column והאלמנט מקבל order מתאים.

זה לא רק משחק של סלקטורים. זה פירוק של התנהגות שנראית כיחידה אחת - חיפוש, קטגוריות, מצב ממשק וספירה - לחלקים שהדפדפן כבר יודע לבצע. גם כפתורי הקטגוריות מגיבים לחיפוש דרך :has() ו-:placeholder-shown: הם נעשים שקופים ולא לחיצים כשיש ערך. כל פעם שנדמה שחייבים עוד state, אביתר שואל קודם מה ה-HTML וה-CSS כבר נותנים לו.

ספריית UI נבחנת גם במה שהצרכן צריך להתקין

משם ההרצאה זזה אל המחיר של הפצה. כשמוציאים ספריית UI ל-NPM, לא מספיק שהקומפוננטה עובדת אצלכם. צרכנים צריכים להבין מאיפה לייבא CSS, אם נדרש CSS Modules, אם יש מגבלה של bundler או צורך ב-client-side בלבד. זו החוויה שאביתר רוצה לצמצם עם flairup, ספריית CSS-in-JS שהוא מתאר ככלי למחברי packages.

הוא מדגים תמיכה ב-media queries, hover, pseudo selectors, pseudo elements ואנימציות, לצד style de-duplication. לא כל ספרייה צריכה להיבנות כך. הטענה הפרקטית יותר היא שה-API של הספרייה כולל גם את נתיב ה-styling שלה. dependency נוסף, הוראת התקנה מוזרה או תאימות שבורה אינם הערת שוליים. עבור מי שמתקין את ה-package, הם המוצר.

לא כל רעיון מוזר הוא פתרון לפרודקשן

בסיום אביתר מוריד את ההייפ מהדמו. CSS Search לא בהכרח יציל פרויקט אחר, ולדבריו אין לו בהכרח הרבה production value מחוץ להקשר המדויק הזה. הערך הוא במוכנות לבדוק הנחה ישנה. לפעמים "אל תעשה את זה" הוא כלל שנולד מסיבה טובה. לפעמים הוא הגיע בירושה, בלי שאף אחד עצר לבדוק אם הסיבה עדיין קיימת.

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

השורה התחתונה

  • סקייל אמיתי חושף bottlenecks שהדמו לא מראה.
  • לפני שמחליפים טכנולוגיה, בדקו מה גורם בפועל לעבודה החוזרת מול ה-DOM.
  • ARIA labels ו-CSS selectors יכולים להיות חומר גלם שימושי, לא רק שכבת נגישות ועיצוב.
  • :has(), counters ו-pseudo-elements פותחים אפשרויות, אבל לא פוטרים ממדידה.
  • בספריית UI, חוויית ההתקנה וה-styling הן חלק מה-API.
  • רעיונות מחוץ לקופסה טובים כשבודקים את ה-tradeoffs שלהם, לא כשמכריזים שהם פתרון לכל דבר.

"אחד הדברים שאני הכי אוהב לעשות זה לבנות פרויקטי אופן סורס ולעשות שטויות."

- אביתר אלוש

"זה לא כיף, זה לא חוויה נעימה."

- אביתר אלוש

"מה אם לא הייתי צריך להסיר את האלמנטים האלה מהדום?"

- אביתר אלוש

"זה אומר שאנחנו לא עושים שינויים בדום עצמו,"

- אביתר אלוש
[ פרקי ההרצאה ]
  1. 00:00:00
    כשפרויקט אופן סורס פוגש סקייל
    אביתר מציג את Emoji Picker React ואת בעיית החיפוש שנחשפה כשספרייה נמצאת באלפי אפליקציות.
  2. 00:02:30
    למה עדכון DOM לכל אימוג'י נהיה איטי
    הדמו חוזר לגרסה האיטית ומראה למה הסרה, החזרה או שינוי class של אלמנטים רבים פוגעים בתגובה.
  3. 00:06:28
    להשאיר את ה-state מקומי
    במקום להעביר את מונח החיפוש לכל הפיקר, החיפוש מחזיק value מקומי ומזריק style לפי הצורך.
  4. 00:12:45
    חיפוש בעזרת CSS attribute selectors
    ARIA labels של האימוג'ים הופכים למקור החיפוש: מסתירים הכול ומציגים רק התאמות חלקיות לערך שהוקלד.
  5. 00:15:49
    קטגוריות ריקות ו-:has()
    עם :has() ו-:not() אפשר להסתיר קטגוריה שאין בה התאמה, בלי לנהל רשימת תוצאות ב-JavaScript.
  6. 00:22:25
    לספור תוצאות ב-CSS
    CSS counters ו-pseudo-elements מציגים את מספר התוצאות וממחישים עד כמה אפשר לדחוף את הכלי.
  7. 00:28:30
    flairup והמחיר של ספריות UI
    אביתר עובר לאתגר של styling בחבילות NPM ומציג את flairup ככלי CSS-in-JS למחברי packages.
  8. 00:33:20
    לצאת מהקופסה בלי להפוך את זה לדוגמה
    הסיום מחזיר את הפוקוס לחשיבה: לא כל פתרון מוזר מתאים לפרודקשן, אבל מותר לבדוק את מה שאמרו לא לעשות.
[ נושאים ]

מה כוסה בהרצאה

#Open Source #Career
[ שאלות נפוצות ]

שאלות מההרצאה

למה חיפוש באימוג'י פיקר ב-React עלול להיות איטי?

במקרה שאביתר מציג, יש יותר מ-1,800 אימוג'ים. כשהערך של החיפוש עובר ב-state לכל הרכיבים, כל שינוי דורש לסנן ולעדכן הרבה אלמנטים ב-DOM. הסרה, החזרה או שינוי של class בכל אלמנט הם חלק מהמחיר, במיוחד בחומרה חלשה.

איך אפשר לחפש בלי JavaScript על רשימת אלמנטים קיימת?

הדמו משתמש ב-ARIA label שכבר נמצא על כל אימוג'י. CSS attribute selector מסתיר את כל האימוג'ים ואז מציג את אלה שה-label שלהם מכיל את מונח החיפוש. ה-value עצמו עדיין נשמר מקומית בקומפוננטת החיפוש, אבל אין עדכון state גלובלי שמפעיל מחדש את כל הרשימה.

מה עושה :has() בפתרון של אביתר אלוש?

אחרי שמסתירים אימוג'ים שלא התאימו, :has() מאפשר לבדוק אם בתוך קטגוריה נשאר אימוג'י תואם. עם :not() אפשר להסתיר קטגוריות ריקות. אביתר גם מזהיר שהשימוש הזה אינו בהכרח CSS יעיל לכל אפליקציה, ולכן זו בחירה שצריך לבדוק בהקשר.

מה העיקרון שאפשר לקחת מפרויקט האופן סורס הזה?

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

[ קהילה ]

רוצה לדבר על ההרצאה?

הצטרף לקהילת המפתחים שלנו ב-WhatsApp.