אינטרנט ישראל
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
ראשי » פיתוח אינטרנט » פתרונות ומאמרים על פיתוח אינטרנט » מיקרו פרונטאנד

מיקרו פרונטאנד

רן בר-זיק אוגוסט 1, 2022 8:19 pm 6 תגובות

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

כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.

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

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

איזו בעיה מיקרו פרונט אנד פותרת?

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

תיאור כללי של דרך הפתרון

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

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

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

איזו בעיות מיקרו פרונט אנד כן יוצר לנו?

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

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

דוגמה טכנית – איך זה עובד

כדי להדגים אני אשתמש בריאקט (אפשר כמובן לממש בכל פריימוורק/ספריה) ו Create React App. אני אצור מיקרו פרונטאנד של footer ושל header.

ראשית אני אצור תיקיה ובתוכנה אצור את הקונטיינר ואת שתי האפליקציות המכילות את ה-footer ואת ה-header:

npx create-react-app container –template typescript
npx create-react-app header –template typescript
npx create-react-app footer –template typescript

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

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

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

טעינה בזמן ריצה – עם src – במקרה הזה זו בחירה די קלאסית

<script src="https://footer.example.com/bundle.js"></script>
<script src="https://header.example.com/bundle.js"></script>

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

אנחנו נבחר בטעינה בזמן ריצה עם src. בואו וניצור את footer ואת header. עבור הדוגמה אני רק אכניס טקסט של This is Footer במיקרופרונט אנד של הפוטר וטקסט של This is Header במיקרופרונט אנד של ה-Header.

עורך קוד שיש בו דוגמה של ה-App.tsx של כל מיקרופרונט אנד עם This is the Header באפליקצית Header.
עורך קוד שיש בו דוגמה של ה-App.tsx של כל מיקרופרונט אנד עם This is the Header באפליקצית Header.

אני יכול להריץ כל אפליקציה כזו בנפרד עם npm start או yarn start. תראה כמובן אפליקציה מאוד מאוד משמעותית אבל כזו שרצה בפורט 3000:

אני יכול כמובן לבחור את הפורט שבו האפליקציה רצה ואעשה זאת. אני אבחר את פורט 3001 עבור ה-Header ופורט 3002 עבור ה-Footer. פורט 3000 יישמר עבור הקונטיינר. איך אני בוחר את הפורט? באמצעות הגדרה של PORT. למשל:

  "scripts": {
    "start": "PORT=3002 react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

נגדיר PORT=3002 בפוטר ו-PORT 3001 ב-header.

השלב השני הוא למנוע code splitting. אנחנו צריכים קובץ ג'אווהסקריפט יחיד. את זה אי אפשר לעשות עם create react app שהוא לא ejected (כלומר שהסקריפטים של create react app מנותקים ממנו) ואנו צריכים לשנות את קבצי הקונפיגורציה על מנת לעשות את זה. איך נעשה את זה? אפשר עצמאית אבל אני מעדיף עם react-app-rewired – מודול קטן וחביב שמאפשר לשנות קונפיגורציה ואיך Create React App מתנהגת גם מבלי לעשות לה eject. אני אתקין אותה גם ב-footer וגם ב-header.

npm install react-app-rewired

אני אצור config-overrides.js ב-root של כל מיקרו פרונטאנד (כלומר גם ב-header וגם ב-footer) ושם אני אורה ל-create react app ליצור קובץ אחד:

module.exports = {
  webpack: (config, env) => {
    config.optimization.runtimeChunk = false;
    config.optimization.splitChunks = {
      cacheGroups: {
        default: false,
      },
    };

    config.output.filename = "static/js/[name].js";

    config.plugins[5].options.filename = "static/css/[name].css";
    config.plugins[5].options.moduleFilename = () => "static/css/main.css";
    return config;
  },
};

זה בעצם קינפוג של הוובפק כדי שיהיה לנו entrypoint אחיד.

אחרי שעשיתי את זה, אני אחליף את הפקודה react-scripts ב-react-app-rewire ב-package.json והוא יראה כך:

{
  "name": "footer",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.3.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.11.47",
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "react": "^18.2.0",
    "react-app-rewired": "^2.2.1",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.7.4",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "PORT=3002 react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

השינויים האלו משפיעים על הבילד בלבד. אם אני עכשיו אריץ npm run build, אני אוכל לראות בקובץ asset-manifest.json שיש לי entrypoint אחיד:

{
  "files": {
    "main.css": "/static/css/main.css",
    "main.js": "/static/js/main.js",
    "static/js/787.69859e86.chunk.js": "/static/js/787.69859e86.chunk.js",
    "index.html": "/index.html",
    "main.css.map": "/static/css/main.css.map",
    "main.js.map": "/static/js/main.js.map",
    "787.69859e86.chunk.js.map": "/static/js/787.69859e86.chunk.js.map"
  },
  "entrypoints": [
    "static/css/main.css",
    "static/js/main.js"
  ]
}

השלב האחרון הוא לשנות ה-ID של אלמנט האב בכל אפליקציה – גם ה-Header וגם ה-Footer. איך עושים את זה? מאוד פשוט – ב-index.html בתיקית public במקום:

<div id="root"></div>

במיקרו פרונטאנד של Footer וגם במיקרו פרונטאנד של Header. חשוב לבחור שם אחיד עם סיומת אחידה למשל:

<div id="footerApp"></div>

וגם:

<div id="headerApp"></div>

אחרי שהחלפנו את השם ב-index.html, חשוב לא לשכוח ולהחליף את השם גם ב-index.tsx מ:

document.getElementById('root') as HTMLElement

אל:

document.getElementById('headerApp') as HTMLElement

ובהתאמה:

document.getElementById('footerApp') as HTMLElement

נבדוק באמצעות npm start שהכל עובד ובאמת כל השינויים הם כמו שצריך. כל מיקרופרונט אנד יכול לרוץ עצמאית בפורט שלו. 3001 ל-header ו-3002 ל-footer.

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

REACT_APP_HEADER_HOST=http://localhost:3001
REACT_APP_FOOTER_HOST=http://localhost:3002

ועכשיו לעבודת הקידוד. במקרה הזה, אנחנו צריכים לטעון את שתי האפליקציות ואת זה אנו נעשה ראשית עם יצירת קומפוננטת טעינה בשם MicroFrontend.tsx שתהיה ב-root של קומפוננטת ה-container:

import { useEffect } from 'react';

interface Props {
  name: string;
  host: string;
}

const MicroFrontend = ({ name, host }: Props) => {
  useEffect(() => {
    const scriptId = `micro-frontend-script-${name}`;

    const renderRemoteJS = async () => {
      const manifest = await fetch(`${host}/asset-manifest.json`).then((res) =>
        res.json()
      );
      const script = document.createElement('script');
      script.id = scriptId;
      script.crossOrigin = '';
      script.src = `${host}${manifest.files['main.js']}`;
      document.head.appendChild(script);
    };

    renderRemoteJS();

  }, [name, host]);

  return <main id={`${name}App`} />;
};

export default MicroFrontend;

הקומפוננטה הזו בסופו של דבר טוענת את main.js לפי כתובת שאנו ניתן לה ויוצרת גם אלמנט אב עם id בדומה ל-HTML של המיקרו פרונטאנד שכבר שינינו בכל אפליקציה בנפרד.

השלב הבא הוא פשוט לקרוא לה במקום המתאים עם המיקרו פרונטאנד שאנו רוצים. איפה? ב-App.tsx של קומפוננטת הקונטיינר:

import "./App.css";
import MicroFrontend from "./MicroFrontend";

const { REACT_APP_HEADER_HOST: headerHost, REACT_APP_FOOTER_HOST: footerHost } =
  process.env;

function App() {
  return (
    <div>
      <MicroFrontend host={headerHost as string} name="header" />
      <MicroFrontend host={footerHost as string} name="footer" />
    </div>
  );
}

export default App;

ו… זה הכל! כדי שהכל יעבוד אנו צריכים לפתוח שלושה אינסטנסים של הקומנד ליין וללחוץ על npm run בכולם. אם הכל יהיה תקין? נראה בפורט 3000 את שני המיקרו פרונטאנד שלנו. כל מיקרו פרונטאנד רץ עצמאית ואפשר לפתח אותו בנפרד. כאשר אני רוצה להעלות לפרודקשן, אני אצור env לפרודקשן שלי שבמקום local host יכיל את הכתובות האמיתיות.

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

כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.
Micro FrontEnd ארכיטקטורה מיקרו פרונטאנד תשתית

6 תגובות

  1. בן הגב אוגוסט 2, 2022 בשעה 10:49 am

    היי,

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

    כל הכבוד!

    • רן בר-זיק הגב אוגוסט 6, 2022 בשעה 1:59 pm

      תודה! 🙂

  2. שמואל הגב אוגוסט 2, 2022 בשעה 12:10 pm

    מה הרווחת מעבר לכך שלקחת את המושג ״קומפוננטה״ ונתת לה מעטפת שחוצצת אותה לחלוטין מהסביבה?
    באותה מידה יכולת לתת בעלות על קומפוננטות, ולקבוע שצוות A לא נוגע בקומפוננטות של צוות B.

    • רן בר-זיק הגב אוגוסט 6, 2022 בשעה 1:59 pm

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

  3. גיא הגב אוגוסט 2, 2022 בשעה 2:39 pm

    מדהים רן!

    אתה מסביר דברים בצורה כל כך יפה (:

    שני דברים:

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

    2. ״זה טיפה'לה בעייתי לתקשר בינהם עם צריך ושנית יש מגבלות של אייפריים״ -> שנה ל״אם״ בבקשה.

    ישר כח!
    בהצלחה בחיפוש העבודה, מקווה שתגיע אלינו בסופו של דבר (:

    • רן בר-זיק הגב אוגוסט 6, 2022 בשעה 1:58 pm

      תודה רבה! 🙂
      1. נשבע לך לא צועק.
      2. תיקנתי.

השארת תגובה

ביטול

ללמוד ג'אווהסקריפט בעברית

ללמוד לתכנת ג'אווהסקריפט בעברית שגייס יותר משלוש מאות אלף שקל ולמעלה מ-2000 תומכים - בואו ללמוד עכשיו איך לתכנת.

רשימת הנושאים
  • מדריכים
    • ריאקט
    • טייפסקריפט
    • ECMAScript 6
    • ES20XX
    • Node.js
    • Express
    • רספברי פיי
    • Babel
    • docker
    • MongoDB
    • Git
    • לימוד MySQL
    • SASS
    • jQuery
    • CSS3
    • HTML 5
    • SVN
    • LESS
  • פיתוח אינטרנט
    • פתרונות ומאמרים על פיתוח אינטרנט
    • jQuery Scripts
    • jQuery למתקדמים
    • יסודות בתכנות
    • נגישות אינטרנט
  • חדשות אינטרנט
  • מידע כללי על אינטרנט
    • רשת האינטרנט
    • בניית אתרי אינטרנט
  • rss logo

    לכל המאמרים

    לכל המאמרים שפורסמו באינטרנט ישראל משנת 2008 ועד עכשיו.
  • rss logo

    RSS Feed

    משתמשים בקורא RSS? אם כן, עקבו אחרי אינטרנט ישראל באמצעות פיד ה-RSS!
    מה זה RSS?
  • Twitter logo

    עקבו אחרי בטוויטר

    בחשבון הטוויטר שלי אני מפרסם עדכונים מהירים על חדשות בתחום התכנות והיזמות, התרעות על מצבי חירום ורכילות בוערת על תחום הווב.
    מה זה טוויטר?
  • facebook like image

    ערוץ הטלגרם של אינטרנט ישראל

    בערוץ הטלגרם של אינטרנט ישראל אני מפרסם את הפוסטים של באתר וכן עדכונים טכנולוגיים נוספים.
    מה זה טלגרם?
  • github logo

    הפרויקטים שלי בגיטהאב

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

כל הזכויות שמורות לרן בר-זיק ולאינטרנט ישראל | מדיניות הפרטיות של אתר אינטרנט ישראל | אתר אינטרנט ישראל נגיש לפי תקן WCAG 2.0 AA | הצהרת הנגישות של האתר | אבטחת מידע ודיווח על בעיית אבטחת מידע

גלילה לראש העמוד