הבעיה - מודולריות

רציתי לבחון את react-native שפייסבוק הוציאו לאחרונה. היא מבוססת על ספריה אחרת בשם react. כפי שניתן לנחש, react-native מבוססת על react. react היא הספריה לניהול התצוגה, לדבריהם - היא אמורה להיות ה v ב mvc.

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

React.render(
<h1> Hello, world!</h1>,
document.getElementById('example')
);

לא אכביר במילים, הקוד הבא נכתב ב javascript והוא מוסיף תגית “<h1>” לתוך אובייקט DOM בשם example. עם זאת, להלן דברים שעצבנו אותי:

  • העורך הטקסט שלי visual studio code, ממש נדפק בגלל react. חלוקה לקבצים הייתה פותרת לי את זה, כי רק קובץ קטן היה נדפק ולא כל הקוד.
ואני מבין אותו, מיקומן של תגיות XML הוא לא בתוך קוד JS (הן אפילו לא בתוך מרכאות..)
  • כתבתי את כל הקוד בקובץ אחד, מה שגרם להרבה בלאגן.

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

הסיבה שלא רציתי ללנקג’ בצורה כזו היא כי עבדתי על ספריה - לא מצתי דרך לעשות את זה דרך javascript בלי לערב html ולא רציתי שהמשתמש של הספריה יכיר קבצים פנימיים ואת הסדר שלהם. כמו כן לא רציתי לעשות squash לקובץ אחד גדול.

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

הפתרון המגניב - Design Pattern

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

var Module = (function(public_api){
  var this_is_a_var = 3;

  public_api.getAVar = function() { return this_is_a_var }

  return public_api;
} (Module));

console.log(Module.getAVar());

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

  • jquery משתמשת בסימן $ עבור שם המודול
  • underscore משתמשת בסימן, ובכן - ‘_’ עבור שם המודול שלה

ניתן לקרוא עוד - פה.

הפתרון הזה מאפשר להפריד לקבצים ופותר יחסית לרמה טובה את בעית הזיבול של ה scope הגלובאלי, אבל עדיין מציקה לי העובדה שצריך להשתמש בתגיות <script> ויש תלות בסדר שלהן.

הפתרון שעובד - require & browserify

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

אבל node לא ממש עוזר לי, כי הוא לא רץ על הדפדפן. ובכן, לא מדויק, בשביל זה בדיוק המציאו את browserify (אגב, ממליץ להשתמש דווקא ב watchify, שמאפשר עבודה אוטומטית יותר)

הפתרון ש require נותן הוא בעצם להגדיר את התלויות שלנו בתוך הקובץ js עצמו, ולא בתוך קובץ ה html. יש לזה משמעות עצומה בעיקר כי עכשיו ניתן:

  1. להסתיר מהמשתמש את הקבצים הפנימיים של הספריה
  2. להסיר את דאגת המשתמש לגבי סדר התלויות
  3. להשתמש בכלים מגניבים. סתם, לא באמת… הם ממש מגניבים :)

שוב, לא אכנס פה לפרטים יותר טכניים, יש אנשים שעשו את זה טוב ממני.

סוף דבר

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

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

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

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

עם זאת, אני ממליץ לכל מי שעדיין לא יודע JS, ללכת עכשיו וללמוד. javascript היא השפה בעלת השוק עם הגדילה הגדולה ביותר ובנוסף שווה להכיר אותה על מנת להרחיב אופקים ולפתוח את הראש לדרך חשיבה נוספת, גם אם היא דפוקה.

בסופו של דבר, הקוד שלי עדיין לא עובד (בגלל פיצ’פקס). אבל עכשיו יש לי blog post ארוך בנושא :)

אשמח לשמוע את דעתכם