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

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 מאפשר בניה של אפליקציית ריאקט מלאה בלי קומפילציה. בואו נראה איך זה עובד.

1. קוד הפרויקט

אני כותב קובץ בשם index.html עם התוכן הבא:

<!DOCTYPE html>
<html lang="en">
  <head><title>Hello World</title></head>
  <body>
    <main id="app"></main>
  <script type="importmap">
  {
    "imports": {
      "htm/react": "https://ga.jspm.io/npm:htm@3.1.1/react/index.module.js",
      "react": "https://ga.jspm.io/npm:react@18.1.0/dev.index.js",
      "react-dom": "https://ga.jspm.io/npm:react-dom@18.1.0/dev.index.js"
    },
    "scopes": {
      "https://ga.jspm.io/": {
        "htm": "https://ga.jspm.io/npm:htm@3.1.1/dist/htm.module.js",
        "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js",
        "scheduler": "https://ga.jspm.io/npm:scheduler@0.20.2/dev.index.js",
        "scheduler/tracing": "https://ga.jspm.io/npm:scheduler@0.20.2/dev.tracing.js"
      }
    }
  }
  </script>

  <!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
  <script async src="https://ga.jspm.io/npm:es-module-shims@1.5.1/dist/es-module-shims.js" crossorigin="anonymous"></script>

    <script type="module" src="src/main.js"></script>
  </body>
</html>

החלק הראשון אמור להיות לכם מוזר - המנגנון נקרא Import Maps והוא אמור לאפשר בעתיד ניהול תלויות מסודר מתוך ES Modules. כרגע, הבעיה עם תלויות היא שכשאתם כותבים:

import React from 'react';

לדפדפן אין מושג מאיפה להביא את react. ב node או webpack אנחנו משתמשים בקובץ package.json בשביל למפות את השם הזה לקובץ אמיתי, ובתוך דפדפן Import Maps הוא הצעה אחת למימוש כזה מנגנון. אפשר לקרוא עליו כאן: https://github.com/WICG/import-maps

הדבר החשוב הוא שכרגע יש פוליפיל שעובד והרבה כלים שיודעים לקחת קובץ package.json ולהפוך אותם ל Import Maps, וגם ממשק גרפי אונלייני לבניית Import Maps שאפשר למצוא בקישור הזה: https://generator.jspm.io/#.

לכן החלק של ה Import Maps מקביל ל package.json שיהיה לנו בפרויקט webpack קלאסי.

חוץ ממנו יש סקריפט בשם main שמוגדר כמודול והוא נקודת הכניסה ליישום.

בקובץ src/main.js אני כותב את התוכן הבא:

import { html } from 'htm/react';
import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(html`<${App} />`, document.querySelector('#app'));

ופה אנחנו מתחילים לראות את ספריית htm בפעולה. אפשר לראות שהקוד אינו JSX אבל הוא כן מאוד דומה בזכות השימוש ב Template Strings. אגב, חוץ מזה אפשר היה בקלות לטעות ולחשוב שאנחנו בפרויקט וובפאק. כל ה import-ים עובדים רגיל לגמרי.

הקובץ השלישי הוא src/App.js (ולא jsx, כי אין יותר jsx) והוא מקבל את התוכן הבא:

import { html } from 'htm/react';
import React from "react";

export default function App() {
  const [count, setCount] = React.useState(0);
  function inc() {
    setCount(c => c + 1);
  }

  return (
    html`<div>
      <p>Welcome To The Future</p>
      <button onClick=${inc}>Value: ${count}. Click To Increase</button>
    </div>
    `
  );
}

הכל כאן ריאקט רגיל לגמרי, חוץ מה JSX שהפך ל Template Strings שמאוד דומים לו.

2. הרצה

שלושת הקבצים האלה, בלי וובפאק, בלי node.js, בלי בייבל ובלי שום טרנספילציה, רק שלושה קבצים - מספיקים בשביל לקבל יישום ריאקט בדפדפן. אני מפעיל שרת מקומי על פורט 8080 עם הפקודה:

$ npx http-server

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

3. מחשבות לעתיד

כמה מחשבות והערות אחרי כתיבת הקוד הזה:

  1. האתר ga.jspm.io הוא זה שמגיש את קבצי ה JavaScript בפועל והוא מתפקד בתור CDN. זה מאוד מזכיר את החיים בפיתוח ווב לפני עשר שנים, כשבשביל להשתמש בספריה היינו פשוט מדביקים את הלינק אליה ל html. אני מתאר לעצמי שהרבה אנשים יעדיפו עדיין לשמור את כל התלויות האלה אצלם בפרויקט או על cdn בשליטתם, וזה בסדר - ל Import Maps לא אכפת מאיפה הוא מביא את המודולים.

  2. יהיה מעניין לראות אם לאורך זמן אפשר יהיה למצוא הצדקה ל webpack ו babel, כשרוב מה שהם עושים אפשר לעשות היום ישירות בדפדפן. נכון - scss זה חמוד ול TypeScript יש יתרונות, אבל המחיר של הכלים האלה מתחיל להיות משמעותי ככל שהאלטרנטיבה הפשוטה יותר יכולה לתת מענה מלא לרוב הצרכים.