הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

החיים אחרי בייבל

20/05/2022

את AMD הכרתי באמצעות require.js שהיתה להיט באזור שנת 2012 (מה שנקרא, עובד ב IE6) וכנראה גם קצת קודם. מה ש require עשתה היה לאפשר לכם לכתוב בתוך קובץ JavaScript אחד פקודה שתטען קובץ JavaScript אחר. נכון, לפני require היתה את dojo שעבדה על אותו מנגנון אבל זה כבר למתקדמים.

ל Require היה כלי אופטימיזציה שלוקח קבצים שכתובים ב AMD ומאחד אותם יחד לקובץ אחד וככה מצליח לשפר את זמן טעינת העמוד, כי צריך לשלוח פחות קבצים לדפדפן. אחרי זה הגיעו grunt, gulp, webpack ו babel ואנחנו עברנו להשתמש בכתיב ה import/export ולהריץ קוד על קבצי ה JavaScript בתוך שלב האופטימיזציה.

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

נגלגל קדימה ל 2022 והעולם השתנה שוב. היום דפדפנים תומכים תמיכה מלאה ב ES Modules ובטעינת קובץ JavaScript מתוך קובץ אחר. בנוסף תקן HTTP/2 תומך ב Server Push שאומר שאין ייתרון מבחינת ביצועים ל Bundling.

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

הספריה htm מציעה למי שרוצה לוותר על שלב הקומפילציה דרך החוצה, באמצעות תחביר שנראה מאוד דומה ל JSX אבל עובד בדפדפן. שילוב של htm עם כל שאר החידושים בעולם ה web מאפשר בניה של אפליקציית ריאקט מלאה בלי קומפילציה. בואו נראה איך זה עובד.

המשך קריאה

מדריך: איך לבדוק פרויקט vite עם vitest

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

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

במדריך זה אני אצור אתכם פרויקט react עם vite, אתקין את vitest ואכתוב את הבדיקה הראשונה לפרויקט. מוכנים? הנה זה בא.

המשך קריאה

טיפ גיט: נגמלים מ push

18/05/2022
git

ננעלתי מחוץ לבית השבוע. כן, יש לי דלת כזאת שמרגע שסוגרים אותה היא ננעלת, ובלי לשים לב סגרתי את הדלת כשהמפתחות היו בתוך הבית. מהלך בלתי הפיך דומה קורה לי ב git כל פעם אחרי שאני עושה commit - והפעם אני מדבר על push.

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

סיבה שניה (או יותר נכון אילוץ) להפעיל push מיד אחרי commit היא בשביל להפעיל איזה pipeline שיבדוק את השינוי על שרת מרוחק. גם פה אם אפשר שווה לייצר מאגר אחד עבור ה pipelines והבדיקות, ומאגר שני, אמיתי, לצורך שיתוף קוד עם אנשים אחרים.

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

וטיפ לסיום - צעד בדרך לגמילה מ push יכול להיות לבטל את ה upstream tracking של הענף שאתם עובדים עליו, וכך push בלי שם של ענף לא יעבוד. כלומר על ענף קיים שכבר עוקב אחר ענף מרוחק נפעיל:

$ git branch --unset-upstream

ועכשיו ניסיון לעשות push ייכשל עם ההודעה:

fatal: The current branch mybranch has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin mybranch

אתם עדיין יכולים לעשות push אם אתם משוכנעים שזה רעיון טוב תוך ציון שם הענף בצורה מפורשת, במקרה שלי זה:

$ git push origin mybranch

והיתרון הוא שכבר אי אפשר לכתוב בטעות git push מיד אחרי קומיט ואחרי זה לאכול את הלב כשצריך לתקן שגיאת כתיב.

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

17/05/2022

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

המשך קריאה

זה ייקח רק חמש דקות

16/05/2022

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

אבל אלה רק חלק קטן מהמצבים.

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

או בדוגמה אחרת מ bash הפעם, בשביל להסביר למישהי איך לתקן את הסקריפט הזה:

#!/bin/bash

count=0

ls | while IFS= read -r line;
do
  (( count++ ))
done

echo $count

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

חיפוש תשובה של 5 דקות הוא הסחת דעת. כמעט תמיד כשאתם תקועים על "משהו קטן", עדיף להתאמץ ולשים לב למשהו הגדול שנמצא מתחתיו.

שלושה טריקים של foreach שכבר לא כדאי לעשות

15/05/2022

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

המשך קריאה

בואו נמצא מספרים שמחים עם Elixir

14/05/2022

מספר שמח הוא מספר שסכום ריבועי הספרות שלו בסוף יוצא 1. מספר עצוב הוא מספר שסכום ריבועי הספרות שלו לא יוצא 1, לא משנה כמה פעמים מעלים בריבוע ומחברים. ככה יוצא ש 7 הוא מספר שמח, כי כשמעלים אותו בריבוע מקבלים 49, וכשמעלים בריבוע את שתי הספרות של 49 מקבלים 16 ו 81 שסכומם יוצא 97, ממשיכים עוד סיבוב ומקבלים את 49 ו 81 שסכומם הוא 130, ועכשיו אנחנו מתקרבים למשהו כי 3 בריבוע זה 9 ו 1 בריבוע נשאר 1, יחד הם נותנים 10, שסכום ריבועי הספרות שלו הוא פשוט 1 בריבוע ועוד אפס בריבוע כלומר המספר 1.

לעומתו 4 הוא מספר עצוב כי בריבוע הוא נותן 16, וכשמעלים בריבוע את 1 ו 6 מקבלים 1 ו 36, שזה יחד 37, הסכום הבא הוא 58, אחרי זה 89, ואז 145, 42, 20 ושוב 4.

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

המשך קריאה

כשהקוד שכתבת יוצא מסובך יותר ממה שרצית

13/05/2022

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

זה לא אומר שהקוד גרוע.

זה לא אומר שהבעיה קשה מדי וצריך ללכת לפתור בעיות אחרות.

זה לא אומר שאתה לא מתכנת טוב.

זה לא אומר שצריך למצוא ספריה חיצונית שתעשה את הדבר הזה.

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

שטח מת

12/05/2022

אני כותב הרבה ריאקט בתקופה האחרונה, אולי אפילו יותר מדי. כך יצא שכשישבתי לכתוב HTML רגיל הקלדתי בלי לשים לב:

<div />
<div />
<div />
<div />

פתחתי אותו בדפדפן, ה CSS כמובן נשבר לגמרי ואני מסתכל ולא רואה את הבעיה. כאילו שטח מת.

ברכב אפשר לכוון את המראות או לקנות מובילאיי כדי להילחם בשטחים המתים. בקוד, לכל שפת תכנות וסביבה יש את הדרכים שלה ועם הזמן אנחנו לומדים לשים לב ולבנות עוד דרכים כדי לראות טוב יותר. ב HTML שווה להשתמש ב HTML Hint, שאומנם לא זיהה את הבעיה הפעם אבל עוזר לזהות אינסוף בעיות אחרות. דרך שניה היא להתרגל לפתוח את הקובץ בדפדפן עם Inspect Element כדי לראות איך הדפדפן רואה את העמוד. כפתור זה מיד היה מראה את הבעיה כי בחלון ה Inspect אני רואה מה באמת קורה שם:

<div>
    <div>
        <div>
            <div></div>
        </div>
    </div>
</div>

טיפ טייפסקריפט: פיצול ממשקים

11/05/2022

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

function Counter(props: { initialValue: number, step: number }) {
}

עם הגדילה של הקומפוננטה טבעי להוסיף עוד משתנים ב props. לפעמים נוח לנו גם להוציא את כל ההגדרה לקובץ אחר כדי שאפשר יהיה להשתמש באותה רשימת props בעוד מקומות. לכן אחרי כמה שבועות או חודשים של עבודה על הקוד השורה יכולה להפוך ל:

interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
}

function Counter(props: ICounterProps) {
}

ואז ככל שהקומפוננטה ממשיכה לגדול אנחנו עשויים לרצות להוסיף מאפיינים שיש ביניהם קשר. לדוגמה אולי נרצה להוסיף כפתור turbo שיגדיל את ה Counter במספר גדול יותר מה step הרגיל. במצב כזה אני יכול להגיע ל:

interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
  turboButtonText?: string;
  turboStep?: number;
}

function Counter(props: ICounterProps) {
}

מבנה כזה לא רצוי כיוון שהוא לא נותן לקוד שמשתמש בקומפוננטה דרך לדעת שיש קשר בין turboStep ל turboButtonText ושאם מציינים אחד צריך גם לציין את השני. במקום זה עדיף לפצל את ה Interface לשני ממשקים שונים:

interface ICounterProps {
  initialValue: number;
  step: number;
  buttonText?: string;
}

interface ICounterWithTurboProps extends ICounterProps {
  turboButtonText: string;
  turboStep: number;
}

בקוד הקומפוננטה נאפשר לקבל פרמטרים משני הממשקים באמצעות איחוד טיפוסים, ונשתמש ב Casting כדי להפוך את ה props לממשק הכללי ביותר:

function Counter(props: ICounterProps|ICounterWithTurboProps) {
  const {
    initialValue,
    step,
    buttonText = 'Increase',
    turboButtonText,
    turboStep,
  } = props as ICounterWithTurboProps;
}

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

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