es
Feedback
ToCode

ToCode

Ir al canal en Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Mostrar más
1 419
Suscriptores
Sin datos24 horas
Sin datos7 días
+130 días
Archivo de publicaciones
ToCode
1 419
# פיתוח רשימה עם סינון ב Solid JS סוליד היא פריימוורק ריאקטיבית לפיתוח יישומי צד לקוח. היא דומה לריאקט בגלל השימוש ב JSX אבל יש לה מספר היבטים משמעותיים שונים, חלקם לטובה וחלקם פחות לטובה. בואו נראה חלק מהם באמצעות פיתוח קומפוננטה לרשימת פריטים עם סינון ב Solid JS. ## מה אנחנו בונים אפשר להיכנס לקוד הקומפוננטה המלא ולשחק איתו אונליין בקישור: https://bit.ly/3IIWUKe אחרי שתיכנסו תמצאו שם רשימה של ימי השבוע ותיבת סינון. כתיבת טקסט בתיבה תסנן את הרשימה ותציג רק את הימים שמתאימים לטקסט. שחקו עם זה קצת, ובואו נמשיך לדבר על הקוד. ## סיגנלים במקום סטייט רעיון מהפכני ראשון של סוליד הוא זיהוי התלויות האוטומטי - שזה בעצם הריאקטיביות. הרעיון הוא שמידע מסוים (שבריאקט היינו קוראים לו סטייט) יכול להשתנות, ומידע במקום אחר יכול להיות מושפע מהשינוי הזה. בריאקט כל ה Hooks API בנוי על העברת רשימות של תלויות כדי שריאקט יוכל לדעת מה משפיע על מה. בסוליד קריאה של ערך אוטומטית מוסיפה את ה"דבר" הזה לרשימת התלויות, ולכן חישוב הרשימה הוא אוטומטי. בדוגמה שלנו יש לנו תיבת חיפוש והערך בתיבה משפיע על רשימת הימים שצריך להציג. הנה שתי השורות שגורמות לקסם הזה לקרות:
const [search, setSearch] = createSignal('');
const visibleItems = createMemo(
  () => items().
  map((i: string) => i.toLowerCase()).
  filter((i: string) => i.includes(search()))
);
המשתנה search הוא סיגנל - כלומר הוא מחזיק מידע שכשמידע זה משתנה צריך להודיע לכל מי שתלוי במידע על השינוי. בשורה הבאה אני מפעיל את הפונקציה createMemo שיוצרת משתנה שתלוי במידע. אני לא אומר לה באיזה מידע הוא תלוי, ובאופן אוטומטי סוליד מזהה מתי הפעלתי את הפונקציה search() שמחזירה את המידע בסיגנל, ויוצר את הקשר. הפעלת הפונקציה setSearch תשלח את הסיגנל ותודיע ל visibleItems שעליו להתעדכן. מסיבה זו כל משתני הסטייט בסוליד הם בעצם פונקציות, והם יכולים להיווצר בכל מקום בתוכנית ולא רק בתוך קומפוננטות. כך הגדרתי את items בקוד הראשי של התוכנית:
const [items, _] = createSignal(['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']);
סוליד עובד כברירת מחדל ב TypeScript, ולכן בשביל לקבל משתנה סטייט בתור Property של קומפוננטה אני צריך לתת לו את הטיפוס הנכון. הטיפוס נקרא Accessor והוא מוגדר בספריית סוליד עצמה. בגלל זה כתבתי שם:
interface IFilteredListProps {
  items: Accessor<Array<string>>;
  renderItem?: (item: string) => Node
}
בשביל לעדכן את search יש לנו פונקציית set, ממש כמו בריאקט. זאת תיבת החיפוש שמתאימה למשתנה:
<input type="text" value={search()} onInput={e  => setSearch(e.currentTarget.value)} />
שווה לשים לב שבניגוד לריאקט, כאן האירוע נקרא input ולא change, כדי להתאים לטרמינולוגיה של ה DOM. ## לולאות טריק שני של סוליד שעובד אחרת מריאקט הוא הלולאות. שימו לב לקוד-
<ul>
  <For each={visibleItems()}>
    {(item, _index) => (
      renderItem(item)
    )}
  </For>
</ul>
במקום map רגיל יש לנו קומפוננטה בשם For, שמקבלת מאפיין each שהוא ערך ריאקטיבי, ובנוסף בתור הילד היחיד היא מקבלת פונקציה שמחזירה את האלמנט שמתאים לכל פריט. המימוש שלי ל renderItem נראה ככה:
function defaultRenderItem(item: string) {
  return (
    <li>
        <input type="checkbox" />
        {item}
    </li>
  )
}
אז מה הסיפור עם הקומפוננטה, ולאן נעלם המאפיין key? נתחיל בקומפוננטה - לסוליד יש קטע שרק קוד שכתוב בתוך קומפוננטות מושפע משינויים ריאקטיביים בסיגנלים. אצלנו יש שני סיגנלים items ו search, מהם מושפע סיגנל שלישי בשם visibleItems וממנו מושפע הקטע שבתוך קומפוננטת ה For. כש search או items יתעדכן, אז visibleItems יחושב מחדש ובתגובה קומפוננטת ה For שמושפעת ממנו תחושב מחדש. זה ריאקטיביות כל הדרך. הסיפור כאן מאוד שונה מריאקט, שם כל שינוי במשתנה state או prop כלשהו יגרום להרצה מחדש של כל פונקציית הקומפוננטה, ולא רק של החלק שמושפע מאותו השינוי. ברמת הביצועים בהנחה שהחיבורים בין הסיגנלים לדברים שמושפעים מהם לא גוזלים יותר מדי זיכרון, אז פעולת ה render של סוליד יותר מדויקת מזו של ריאקט ולכן יש פחות עבודה מיותרת של השוואות בין Virtual DOMs. למעשה בגישה הריאקטיבית של סוליד לא צריך Virtual DOM בכלל.

ToCode
1 419
# ממלכת הוודאות רוב הזמן בעבודה על בעיית תכנות לא מושקע בהקלדה. בעיות התכנות המעניינות הן כאלה שאתה פשוט לא יודע מה להקליד כדי לפתור אותן, ולכן רוב הזמן מושקע במחקר: מחפשים רעיונות (בראש או ברשת), כותבים ומנסים אותם, רואים אם זה עובד ואז מתקנים, משפרים או עוברים לרעיון הבא. ורוב הזמן אנחנו לא יודעים כמה זמן ייקח עד שנגיע לפיתרון שעובד. ויש משהו ממכר בממלכת האי וודאות הזאת, אפילו מותח. זה מרגיש שאנחנו עושים משהו חשוב - בונים עוד פיצ'ר, מתקנים עוד באג. עושים את העבודה. תשוו את זה לכל הדברים שאנחנו לא עושים בפרויקט, אבל חשובים לא פחות: 1. כתיבת תיעוד. 2. עריכת היסטוריית הקומיטים בגיט כדי שתשקף נכון את העבודה שעשינו. 3. כתיבת בדיקות. 4. ריפקטורינג לפיתרון כדי שיהיה קל לתחזק אותו בהמשך. 5. טיפול במקרי קצה. 6. טיפול בשגיאות ויצירת Flows מתאימים למשתמשים במצבי שגיאה. כל אחד מששת הסעיפים ברשימה הוא הצלחה וודאית - מרגע שמתחילים אותו הוא בטוח יצליח. ששת הסעיפים יחד יחזקו את הפיתרון הנוכחי שכתבנו, ויהפכו קוד שבמקרה עובד למנגנון בסיסי במערכת שאפשר להסתמך עליו ולבנות בעזרתו מנגנונים נוספים. גם מבחינת השקעת זמן - החלק הכי קשה באי וודאות הוא האינטרקציה בין הפיתרון החדש שאני כותב לקוד הקיים במערכת. חיזוק הקוד הקיים באמצעות תיעוד, בדיקות וטיפול בכל מקרי הקצה, מצמצם משמעותית את האי וודאות ומאפשר לי להתקדם יותר מהר בפיתרון בעיות חדשות. אבל עזבו אתכם הצלחות וודאיות וקוד נכון... למי יש זמן לזה... יאללה מה הבאג הבא?

ToCode
1 419
# מעקב אחר שינויי גודל בדפדפן עם ResizeObserver טכניקות עיצוב ריספונסיבי רבות כוללות שינוי גדלים של אלמנטים כשהעמוד משנה גודל, ובאמת עם CSS אפשר להגיע לתוצאות טובות כשמה שמעניין אותנו הוא המסך כולו. אבל ברגע שאנחנו מתחילים לשלב JavaScript העסק הופך יותר מסובך - קוד JavaScript יכול לגרום לשינוי גודל של אלמנט מסוים בעמוד, בלי לשנות את הגודל של כל החלון. ממשק חדש שזמין כבר בכל הדפדפנים המרכזיים בשם Resize Observer יכול לעזור לנו לזהות שינויים כאלה ולהגיב אליהם, וכך אנחנו לא צריכים כל פעם שכותבים JavaScript בעמוד לחשוב על איזה גדלים הוא עלול להשפיע. בואו נראה דוגמה לממשק זה ודרכה גם אסביר איך הוא עובד. ## דוגמה: עדכון צבע רקע לפי שינוי גודל תיבת טקסט הקודפן הבא כולל דוגמה ראשונה ל Resize Observer שמשנה את צבע הרקע של המסך עם כל שינוי בגודל ה text area. אתם יכולים לנסות למשוך בידית של ה textarea כדי להגדיל ולהקטין אותו ולראות את הצבע משתנה: <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/WNdRdJe?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/WNdRdJe"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> הקוד של ResizeObserver הולך לפי תבנית פשוטה: 1. ביצירה אנחנו מעבירים Callback Function שתיקרא כל פעם שאלמנט שעוקבים אחריו משנה גודל. 2. אנחנו קוראים לפונקציה observe ומעבירים לה אלמנט כדי להתחיל לעקוב אחרי שינויים בגודל של אלמנט מסוים. זה קוד ה JavaScript:
const textarea = document.querySelector('textarea');

const observer = new ResizeObserver((entries) => {
  for (let entry of entries) {
    if (entry.target === textarea) {
      const area = entry.contentRect.width * entry.contentRect.height;

      if (area < 15000) {
        document.body.style.backgroundColor = 'yellow';
      } else if (area < 30000) {
        document.body.style.backgroundColor = 'blue';
      } else if (area < 100000) {
        document.body.style.backgroundColor = 'green';
      } else {
        document.body.style.backgroundColor = 'purple';
      }
    }
  }
});

observer.observe(textarea);
כמה החלטות עיצוביות של הממשק ששווה לשים לב אליהן: 1. ה Callback Function מקבל מערך של Entries. זה מערך בגלל שאפשר להשתמש באותו ResizeObserver כדי לעקוב אחר שינויי גודל במספר אלמנטים. כל תא במערך מייצג גודל שהשתנה באיזשהו אלמנט שעקבנו אחריו. 2. כל Entry מכיל את האלמנט שהשתנה ואת הגודל החדש שלו. בצורה כזאת לא צריך לתשאל מחדש את ה DOM לגבי הגודל, מה שמביא לביצועים טובים יותר.

ToCode
1 419
וזה בדיוק הפיתרון שאנחנו צריכים ... אבל. הבעיה איתו שצריך כל פעם שכותבים בדיקה לזכור לעדכן את update_calendar ולהחליף אותה במימוש הפיקטיבי. אם הבדיקה נכשלת עם המימוש האמיתי כנראה שתעלו על הבעיה די מהר, אבל אם הבדיקה מצליחה, רק יותר לאט, אז הרבה יותר קשה להבין שמשהו לא בסדר. ## שינוי התנהגות לבדיקות באמצעות Factory וזה מביא אותנו לתבנית שרציתי להראות פה בפוסט - תבנית המפעל. הרעיון שבמקום ליצור אוביקט חדש דרך הבנאי הרגיל שלו, אני אבנה פונקציה חדשה שיוצרת אוביקט Meeting עבור הבדיקות. משהו כזה:
def create_test_meeting():
    m = Meeting()
    m.update_calendar = MagicMock()
    return m
עם הזמן וכשהמערכת גדלה אני יכול ליצור עוד ועוד פונקציות כאלה, אפשר לארגן אותן במחלקה של TestFactory או ליצור איזושהי פונקציית כניסה מרכזית שמפעילה את פונקציות היצירה לפי המחלקה שקיבלה:
def create(what):
    if what == Meeting:
        return create_test_meeting()
    elif what == Message:
        return create_test_message()
הדבר החשוב והפשוט הוא במקום למלא את קוד הבדיקות שלכם באותן 2-3 שורות שחוזרות על עצמן בכל בדיקה, עדיף לרכז את מאמצי יצירת אוביקטי הבדיקה במקום אחד, ושם גם לשמור את ה mock-ים, כי גם קוד בדיקה צריך תחזוקה. נ.ב. המודול factory boy הוא ספריית פייתון שתעזור לכם לבנות Test Factories. הוא מבוסס על פקטורי-בוט של רובי (שבעבר היה נקרא factory girl ולכן השם).

ToCode
1 419
# תבנית Factory לבדיקות (או: איפה שמים את ה Mock) אם יש לכם קוד שצריך להתנהג בצורה מסוימת במערכת ה"רגילה" ובצורה אחרת בבדיקות, הרבה פעמים לא נרצה ללכלך את הקוד הרגיל ב if-ים שיבדקו באיזה מצב אנחנו. תבנית Factory יכולה לתת פיתרון פשוט לבעיה. ## קוד הדוגמה המחלקה הבאה בפייתון יכולה לייצג "פגישה". פגישה אפשר לקבוע לאיזשהו תאריך בעתיד, וכל פעם שקובעים פגישה המערכת אוטומטית מעדכנת את היומן בענן שלכם בגוגל קלנדר:
import time
from datetime import datetime, timedelta

class Meeting:
    def schedule(self, start_time, duration):
        print("scheduling meeting")
        if start_time < datetime.now():
            raise Exception("Meeting scheduled in the past")

        self.update_calendar()

    def update_calendar(self):
        print("adding the new meeting to google calendar")
וכן אני יודע שכרגע update_calendar רק מדפיסה איזו הודעה, אבל דמיינו איתי שיש שם באמת קוד תקשורת לגוגל. הצלחנו לדמיין? יופי, נמשיך. כשאני ניגש לבדוק כזה קוד אני רוצה לבדוק את הפונקציונאליות הפנימית שלו - למשל לבדוק שאי אפשר לקבוע פגישה לתאריך בעבר. אז אני יכול לכתוב קוד בדיקה שיוצר פגישה ומנסה לקבוע אותה לכל מיני תאריכים:
class TestMeeting(unittest.TestCase):
    def test_cant_schedule_on_past_dates(self):
        m = Meeting()
        with self.assertRaises(Exception):
            m.schedule(datetime.now() - timedelta(days=10), timedelta(hours=2))

    def test_can_schedule_on_future_dates(self):
        m = Meeting()
        m.schedule(datetime.now() + timedelta(days=10), timedelta(hours=2))
        assert(True)
הבדיקות האלה עוברות יופי, אבל יש רק בעיה אחת - הן גורמות להפעלה של update_calendar. אני יודע מה אתם חושבים, update_calendar כרגע לא עושה כלום, אבל אני מזכיר שאנחנו מדמיינים שהיא פונה לגוגל ומנסה לעדכן את היומן. התוצאה של הרצת הבדיקות תהיה או האטה של הבדיקות בגלל קוד תקשורת לא רלוונטי, או, יותר גרוע, פגישות פיקטיביות ביומן. ## שינוי התנהגות לבדיקות מבפנים דרך התמודדות ראשונה עם מצב כזה יכולה להיות לשנות את הקוד המקורי כדי לאפשר "מצב בדיקות", לדוגמה אני יכול להוסיף בנאי שמקבל פרמטר אם אני בסביבת בדיקות, שומר אותו כמשתנה מחלקה ואז schedule תוכל להסתכל על המשתנה הזה ולדעת אם לעדכן את גוגל או לא. בקוד זה נראה ככה:

class Meeting:
    def __init__(self, test_mode=False):
        self.test_mode = test_mode

    def schedule(self, start_time, duration):
        print("scheduling meeting")
        if start_time < datetime.now():
            raise Exception("Meeting scheduled in the past")

        if not self.test_mode:
            self.update_calendar()

    def update_calendar(self):
        print("adding the new meeting to google calendar")
ואז בקוד הבדיקה אני יוצר את הפגישה עם:
def test_can_schedule_on_future_dates(self):
  m = Meeting(True)
  m.schedule(datetime.now() + timedelta(days=10), timedelta(hours=2))
  assert(True)
אבל זה בדיוק הפיתרון שאנחנו לא רוצים לכתוב - אין לקוד המחלקה Meeting שום סיבה לזכור אם אנחנו במצב בדיקה, ומנגנון כזה הוא פתח לבאגים בעתיד. ## שינוי התנהגות לבדיקות מבחוץ יותר מומלץ לשנות את ההתנהגות מבחוץ - כלומר מתוך קוד הבדיקה. בפייתון אני יכול לעשות את זה בקלות, בגלל שאפשר לשנות מתודות אפילו אחרי יצירת האוביקט. קוד הבדיקה הבא מחליף את update_calendar במימוש ריק וכך מריץ את הבדיקה בלי החלק המיותר מהקוד:
class TestMeeting(unittest.TestCase):
    def test_cant_schedule_on_past_dates(self):
        m = Meeting()
        with self.assertRaises(Exception):
            m.schedule(datetime.now() - timedelta(days=10), timedelta(hours=2))

    def test_can_schedule_on_future_dates(self):
        m = Meeting()
        m.update_calendar = MagicMock()
        m.schedule(datetime.now() + timedelta(days=10), timedelta(hours=2))
        assert(True)

ToCode
1 419
# גם מבנה הפרויקט הוא תלות בין כל הפעמים שאנחנו מריצים npx npm-check-updates או bundle update או אפילו pip install -U, יש דבר אחד שנוטים לשכוח - גם מבנה הפרויקט הוא תלות, וגם אותו אפשר לשדרג: 1. אולי יש לכם מערכת Build מאוד מתוחכמת שבניתם לבד ב gulp בימים שהוא עוד היה אופנתי, והיא עדיין עובדת אבל כבר אי אפשר לשנות בה כלום. 2. אולי יש לכם סט בדיקות ממש מוצלח שעדיין כתוב בג'סמין ומורץ עם karma. 3. אולי יש לכם סקריפט שפשוט בונה את הפרויקט על מכונת הפיתוח ומעתיק לשרת את הקבצים עם rsync. ולמה בעצם חשוב לשדרג את מבנה הפרויקט? הרי מה שעובד לא נוגעים וכו' לא? אז ככה: 1. היום יש דרכים יותר טובות לעשות את מה שלפני שנתיים היה קשה. בבניה של פרויקט ווב אולי כבר לא צריך לטרנספל לתמיכה ב IE6. אולי כבר אפשר להשתמש ב HTTP/2 Server Push כדי לשפר ביצועים. 2. לאינטרנט יש נטיה להפוך מידע עדכני לזמין יותר, לפחות במה שנוגע לשאלות טכניות. לכן יהיה לי יותר קל למצוא טיפים על ג'סט מאשר על ג'סמין, ועל webpack במקום על gulp. 3. מתכנתים מכירים את הכלים החדשים מפרויקטים אחרים, ולאט לאט יהיה יותר קשה למצוא מתכנתים שעדיין זוכרים את הכלים הישנים. ככל שהתהליך הזה קורה ככה יותר קשה לכם להוסיף או לעדכן קוד בחלק של הגדרות הפרויקט. שידרוג מבנה הפרויקט זו משימה אפילו יותר קשה משידרוג התלויות הרגיל, כי בדרך כלל זה יכריח אותנו לשכתב אזורים בקוד שכמעט לא נוגעים בהם. אבל אם הפרויקט שלכם חי כבר כמה שנים, שידרוג כזה יהיה מאוד מורגש בעבודה היום יומית.

ToCode
1 419
# עלות שקועה עד גירסה 6 של ריילס לא היתה דרך להריץ בדיקות בצורה מקבילית. לא היתה דרך זו מילה גדולה, גיטהאב כתבו אינסוף קוד כדי לעקוף את הפריימוורק ולהריץ את הבדיקות בצורה מקבילית, אבל זה היה גיטהאב. בסוף נמאס להם לעקוף מגבלות של הפריימוורק ובגירסה 6 של ריילס הם כבר תרמו את כל המנגנון המתוחכם שלהם. עכשיו בואו נחזור אליכם ונניח שגם אתם, כמו גיטהאב, בניתם מנגנון סופר מתוחכם להרצת בדיקות בריילס בצורה מקבילית. מה עושים כשריילס יוצאים עם גירסה 6 ומציעים הרצת בדיקות במקביל built in? האם אתם זורקים את כל מה שכתבתם ועוברים לפיתרון הסטנדרטי? או ננסה תרגיל יותר פשוט - נניח שישבתם שבועיים כדי לבנות בלוג שקורא קבצי markdown מאפס, ואז אחרי שבועיים של עבודה כשיש לכם גירסה ראשונה עובדת אתם מגלים את hugo. האם תזרקו את כל מה שעשיתם ותתחילו מחדש עם הוגו? או שכבר יש לכם משהו שעובד וחבל לוותר על זה? דרך טובה להסתכל על החלטות כאלה היא לשים לב שהעלות שכבר השקעתם היא עלות שקועה. שקועה, כלומר שהיא במים, היא לא תחזור יותר. לעולם לא תקבלו בחזרה את השבועיים שהשקעתם בהקמת פלטפורמה לבלוג או את החודשיים שהשקעתם בהקמת סביבה להרצת בדיקות במקביל בריילס. ועכשיו יש לנו החלטה חדשה לקבל- בהינתן כל מה שאני יודע היום, נניח שמישהו היה מציע לי בחירה בין שני המנגנונים (הבלוג שלי והוגו), איזה מנגנון הייתי בוחר? האם הייתי מעדיף פיתרון שהושקעו בו שבועיים ועדיין לא נוסה בפרודקשן, או פיתרון שהושקעו בו שנים ויש לו המון משתמשים פעילים? אם כשמישהו אחר היה מציע לי את הפיתרון "שלי" לא הייתי לוקח, אז עדיף לא לקחת גם אם אני הוא זה שמציע. הזמן שהשקעתי לא יהפוך את הפיתרון שלי לטוב יותר.

ToCode
1 419
# טיפ CSS: המאפיין display: contents שני מאפייני ה Layout המתקדמים של CSS, פלקסבוקס וגריד, כוללים מגבלה קצת מעצבנת: הם מכילים את הגדרות העיצוב על הילדים הבלוקים הישירים שלהם. במילים אחרות אם יש לי פס עליון באתר ובו 6 לינקים, וה HTML שלי נראה ככה:
<div class="topbar">
    <a href="#">link #1</a>
    <a href="#">link #2</a>
    <a href="#">link #3</a>
    <a href="#">link #4</a>
    <a href="#">link #5</a>
    <a href="#">link #6</a>
</div>
אז אין בעיה להשתמש ב CSS כזה כדי לסדר את כל הלינקים בתוך פלקסבוקס ולחלק להם את המקום שווה בשווה:
.topbar {
  display: flex;
}

.topbar a {
  padding: 0.2rem;
  flex: 1;
}
אבל אם במקרה החלטתי לחלק את הלינקים ב HTML למספר "בלוקים", רק מבחינה סמנטית או כי היה לי יותר נוח לייצר HTML כזה:
<div class="topbar">
   <div class="part1">
    <a href="#">link #1</a>
    <a href="#">link #2</a>
    <a href="#">link #3</a>
  </div>
  
  <div class="part2">
    <a href="#">link #4</a>
    <a href="#">link #5</a>
  </div>
  
  <div class="part3">
    <a href="#">link #6</a>
  </div>
</div>
אז פתאום כל הפלקסבוקס שלי נשבר. יותר מדויק להגיד שהוא לא נשבר פשוט האלמנטים שמסודרים בפלקסבוקס הם עכשיו הדיבים part1, part2 ו part3, ולא הלינקים המקוריים. הערך contents למאפיין display, שנתמך ברוב הדפדפנים (חוץ מ IE כמובן), מספק דרך קלה "לדלג" על אלמנטים כשמסדרים אותם בתוך פלקסבוקס או גריד. ה CSS הבא יגרום גם ל HTML השני להציג את הלינקים בצורה יפה בתוך פלקסבוקס:
.topbar {
  display: flex;
}

.topbar > div {
  display: contents;
}

.topbar a {
  padding: 0.2rem;
  flex: 1;
}
וככה זה נראה לייב בקודפן: <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/LYeZWjq?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/LYeZWjq"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe>

ToCode
1 419
# מערכת ניהול החבילות של דינו אם יש משהו שקל למתכנתי Node להסכים עליו זה ש npm שבור. הבעיות המרכזיות בו הן: 1. מלחמת שמות נוראית במאגר המרכזי, שמובילה לרישום חבילות פיקטיביות וחבילות מזיקות (כן כמו 17 חבילות שונות לגניבת טוקנים של דיסקורד) 2. הגדרה "גמישה" של גירסאות ב package.json וקובץ package-lock.json שחצי מהאנשים מוחקים אותו כל פעם שיש קונפליקט. 3. תלות במאגר מרכזי לכל בניה (היוש left-pad) 4. התקנת חבילות שכוללות אינסוף זבל בנוסף לקבצים שרצית, ורק מנפחות את node_modules (היוש readline) דינו, הניסיון השני של ריאן דל ליצור סביבת ריצה ל JavaScript מחוץ לדפדפן, מתמודד עם ניהול חבילות חיצוניות בצורה מובנית, מבוזרת ודי מעניינת. דינו משתמש ב import ולא ב require, אבל השוס הגדול הוא שבמקום להעביר ל import שם של חבילה אנחנו מעבירים את ה URL המלא אליה. כלומר בשביל להשתמש ב lodash בתוכנית דינו אני אכתוב:
import * as _ from 'https://cdn.skypack.dev/-/lodash-es@v4.17.21-rDGl8YjBUjcrrAbjNrmo/dist=es2019,mode=imports/optimized/lodash-es.js';

const squares = _.map(_.range(10), (x) => x * x);
console.log(squares);
וזה כבר פותר חלק גדול מהבעיות של npm: 1. בגלל שאני מעביר URL, לא צריך ללכת מכות על שמות. 2. הגדרת הגירסה היא חלק מה URL והיא מאוד ספציפית, כמו בתגיות ה script שהיו לנו פעם בקבצי HTML, לפני שעברנו לוובפאק. וגם אין ולא צריך package.json. גם לא צריך ללמוד את ההבדל בין devDependencies ל peerDependencies ול dependencies רגילים. מה שעושים לו import נטען. 3. לא צריך מאגר מרכזי. 4. מורידים רק מה שמשתמשים בו ולא חבילה שלמה בעיה שכבר אפשר לראות במנגנון הזה היא מה קורה כשהתוכן שב URL משתנה, והאם זה לא ישבור לי את כל הפרויקט. אז האמת שגם על זה הם חשבו ואפשר ליצור קובץ נעילה ששומר hash של המודול, ואז בהורדה הבאה לוודא שה hash לא השתנה. אם נניח קראתי לתוכנית שלי demo.js אז על המכונה שלי במצב פיתוח אני אכתוב:
$ deno cache --lock=lock.json --lock-write -r demo.js
וזה ייצור לי קובץ בשם lock.json על המחשב עם התוכן:
{
  "https://cdn.skypack.dev/-/lodash-es@v4.17.21-rDGl8YjBUjcrrAbjNrmo/dist=es2019,mode=imports/optimized/lodash-es.js": "4a1b35d34b5e0d3ae68aa46ddaabb9700ccecb75b830974db7c46a271f10daa8"
}
עכשיו כשאני אעביר את התוכנית למכונה אחרת אני מעביר איתה את קובץ ה lock.json שיצרתי, ומפעיל שם:
$ deno cache --lock=lock.json  -r 03-with-lodash.js
ודינו יוריד את כל החבילות החיצוניות וישווה את ה hash-ים שלהן מול מה שכתוב בקובץ המנעול. אחרי זה בדרך כלל נריץ עם:
$ deno run --lock=lock.json --cached-only  03-with-lodash.js
וזה מוודא שכל החבילות נטענות רק מה cache ושום דבר חדש לא יורד מהרשת. האם זה יתפוס? זאת השאלה הגדולה. במעבר לדינו נצטרך להתרגל לחפש שוב URL-ים מלאים ב cdn-ים במקום פשוט להתקין עם npm install ושם קצר של חבילה. לאורך זמן הגישה של דינו נשמעת לי יותר יציבה, אבל יהיה קשה להיפרד מ npm install lodash.

ToCode
1 419
# אפשר לראות את ה vimrc שלך? וים הוא עורך טקסט מאוד קונפיגורבילי, ולכן בקהילת הוימיסטים אנשים מאוד אוהבים לשתף (שלא לומר להשוויץ) את קבציה הגדרות ה vim שלהם. הקבצים האלה כוללים רשימות פלאגינים, מיפויי מקשים וכמובן קוד vim script שכל אחד כותב או מעתיק כדי להפוך את ה vim להרבה יותר אטרקטיבי. כשאנחנו קוראים קבצי הגדרות כאלה של אחרים יש לנו שתי אפשרויות- 1. אנחנו יכולים לחפש מישהו שנראה שעובד מהר, ולהעתיק את כל קבצי האתחול שלו כמו שהם. על משקל "אם זה טוב מספיק לדמיאן קונווי זה טוב מספיק בשבילי. 2. אנחנו יכולים להתחיל מאפס, לעבור על קבצי ה vimrc שאנחנו מוצאים ברשת כשיש זמן וכל פעם שמוצאים משהו שאנחנו אוהבים ומבינים להעתיק אותו אלינו. השיטה השניה תביא תוצאות מהר יותר. השניה את התוצאות הטובות יותר.

ToCode
1 419
# לא אותה מטרה בשיחות על קוד פתוח ושימוש בכלים מהרשת הרבה פעמים אנחנו מתמקדים בערך של כל כלי ואיך להגיע לתוצאה, מבחינת המוצר שלנו, כמה שיותר מהר. נקודה אחת שנשארת בצד הרבה פעמים היא המניע של כותבי הקוד הפתוח והאם המניעים שלהם עלולים להתנגש עם המטרות העסקיות שלי. כמה דוגמאות ברשותכם- ## ספריה בלי חידושים היא ספריה מתה לא משנה איזה ספריית קוד פתוח אני אוציא וכמה היא תהיה טובה, ואולי במיוחד אם היא תהיה טובה, אחרי כמה חודשים שאנשים ישתמשו בה ההייפ ירד. זה כבר לא יהיה מדליק לכתוב ב JavaScript Weekly על אותה ספריה, והרבה פעמים ספריות חדשות יצאו עם פיצ'רים קצת שונים או אולי טיפה יותר טובים והספריה שלי תישכח. לכן כותבי קוד פתוח הרבה פעמים נאלצים להמשיך ולהוציא עדכונים לספריה שלהם פשוט כדי להישאר בעניינים, וכמה שהעדכון יותר "שובר דברים" ככה יותר ידברו עליו. וככה אנגולר2 נולדת ושוברת לגמרי את כל מה שהכרנו מאנגולר1; ויו-3 שובר הרבה עקרונות שלמדנו עליהם ב-ויו 2; ריאקט עובר מ React.createClass ל React.Component ואז ל Hooks, ואפילו כשאין ממש מה לחדש אנחנו מקבלים את ריאקט 18 שיוציא Warning אם הקוד שלכם משתמש ב ReactDOM.render במקום ב API החדש שהם המציאו. וזה בגדולים - המצב בספריות הקטנות הרבה יותר מטלטל. שווה להשוות את זה לקוד שכותבים לבד - זה לא נדיר למצוא בחברות קוד שנכתב לפני שלוש או אפילו 5 שנים ועדיין נמצא בפרודקשן ועובד טוב. ## מוצרי קוד פתוח שמהווים חלק ממערך עיסקי סוג שני של בעיות שאנחנו הולכים לפגוש יהיה במוצרי קוד פתוח שנתמכים או מיוצרים על ידי חברות גדולות, או שמהווים חלק ממערך עסקי ושנועדו לקדם אינטרסים עיסקיים של אותן חברות. אז ככה יוצא ש Qt זמין בגירסה חופשית אבל הרבה מהשיפורים בפריימוורק מכוונים ליכולות של Qt שקשורות למובייל, אבל בשביל לכתוב אפליקציית מובייל עם Qt אני כבר צריך רישיון בתשלום. או בעולם של הווב גם Material UI וגם Antd כוללות גירסה בחינם וגירסה בתשלום, וכן חלק מהמטרה בגירסת הקוד הפתוח היא לשכנע אתכם לקנות את הגירסה בתשלום. ## סטארט אפים בקוד פתוח שלב אחד קדימה באינטרסים העסקיים אלה הסטארט-אפים בקוד פתוח. אלה חברות שהן הפסדיות בכוונה, וכל המטרה שלהן היא להיות מספיק גדולות כדי שמתישהו יתחילו לעשות כסף או שמישהו יקנה אותן. אז ככה דוקר יכול להיות מופץ לגמרי בחינם, עד שלמשקיעים נמאס ופתאום Docker Desktop כבר מחייב רישיון אם אתם חברה גדולה. גיטלאב נותנים לכם איחסון גיט חינמי לפרויקטים פרטיים כי הם מקווים יום אחד להימכר כמו גיטהאב, ופייפרקאפס יתנו לכם חלופה ל Intercom בקוד פתוח ובחינם. שילוב מוצרים אלה במוצר שלנו, למרות שהם בקוד פתוח, הוא מתכון בטוח לאסון. החברות האלה יום אחד יימכרו או ייסגרו, ואנחנו במקרה הטוב נישאר עם קוד שהוא מסובך מדי בשביל לתחזק לבד. ## חבילות זדוניות ולסיום הסקירה צריך גם להגיד מילה על אותם אנשים שמעלים קוד פתוח באמת בשביל לעשות נזק - אלה יהיו החבילות ב npm שגונבות ביטקוינים למתכנתים שמתקינים אותן, חבילות שמשתילות דלת אחורית על השרת שלכם או שפשוט יצפינו לכם את הקבצים במחשב וידרשו כופר כדי לשחרר אותם. רק היום עלה עדכון לחבילה node-ipc שעשוי למחוק לכם את כל הדיסק אם אתם נמצאים ברוסיה או בלארוס, כי אוקראינה. לסיכום בבחירה להשתמש במוצרי קוד פתוח וספריות קוד פתוח יש פוטנציאל לקצר זמני עבודה ולהגיע ללקוח מהר יותר - אבל שימו לב שבניה על עבודה של אחרים עלולה להיות מסוכנת, אם לאחרים האלה יש מניעים שונים משלכם. שווה תמיד לחפש את המוטיבציה והיום גם אפשר (ורצוי) לתרום למתכנתי קוד פתוח שעובדים על מוצרים שעוזרים לכם.