מה זה micro frontend?
כשעובדים על אתרים גדולים ומורכבים עם מספר צוותים שלפעמים ממוקמים במקומות שונים ועובדים בזמנים שונים, זה פיתרון מצויין.
מה הרעיון שלו?
באנגולר אנחנו מפרידים רכיבים וכל אחד יכול להתקיים ברשות עצמו, אנחנו יכולים גם להפריד מודולים ולטעון בלוקים של קוד על פי דרישה ועכשיו אנחנו יכולים לטעון פרויקטים שונים על פי דרישה לתוך הפרויקט שלנו.
כל מיני פרויקט יכול להיות דף או מספר רכיבים או רכיב אחד בלבד אם זה מה שאנחנו רוצים.
מה היתרונות שלו?
כל פרויקט יכול להיות ממוקם בנפרד מכל השאר, על שרת אחר אם רוצים.
כל פרויקט מפותח בקצב שלו, וניתן לעדכן את הקוד שלו מבלי לפגוע בכל האתר, אין צורך לעצור את כל האתר רק בשביל לעדכן את אחד הפרויקטים שלו.
כל פרויקט יכול להכתב בשפה שונה לחלוטין, אנגולר, ריאקט, vue, javascript או כל דבר אחר שנרצה.
כך נוכל לנצל את הידע של המפתחים במקום שהם הכי טובים בו ולא נגביל את כל הקבוצות פיתוח לעבוד באותה שפה.
הפרויקטים יותר קטנים ולכן יותר קל להתמצא בהם.
מה החסרונות?
כמו תמיד, אין יתרונות בלי חסרונות 😉
יש צורך לתחזק חלק נוסף בפרויקט שהוא החלק המארח, אנחנו נדבר על זה בהמשך, בתהליך של עבודה במבנה הזה יש צורך להקים פרויקט מארח שהוא זה שמושך אליו את כל שאר החלקים מהפרויקטים השונים.
הפרויקטים האחרים יכולים להבנות בשפות שונות אבל בחלק מהמקרים יש צורך לבנות אותם בצורה מסויימת כדי שיהיה אפשרי לתפקד איתם גם בזמן פיתוח וגם בפריסה לשרת (גם על זה נדבר בהמשך).
בגלל שמדובר בפרויקטים שונים שכל אחד מהם הוא עולם ברשות עצמו, הם לא יכולים לדבר אחד עם השני כמו שאנחנו רגילים – input,output, service, function call וכו', אלא צריך למצוא דרך גלובלית שלא קשורה לפרויקט עצמו להעביר מידע.
במילים אחרות, אפשר לראות שהפרויקט הולך להיות מסובך יותר, אבל אל פחד! ברגע שמבינים את הרעיון וחושבים על מבנה פרויקט מתאים למה שאנחנו צריכים, התהליך נעשה יותר קל וברור.
אני יכול רק להמליץ, אל תבחרו בצורת הבניה הזו אם היא לא נותנת לכם יתרונות ברורים. לא לבחור בה בגלל שזה חדש ומתקדם ועושה רעש, אלא בגלל שיש צורך שרק השיטה הזו יכולה לספק.
אם פרויקט מונוריפו יכול לתת לכם את הפתרון בצורה טובה, הישארו עם זה, הפיתוח יהיה קל יותר ומהיר יותר.
אם יש לכם פרויקט גדול שמפותח על ידי מספר צוותים בעלי לוחות זמנים שונים או צורך במספר שפות שונות אז מיקרו-פרונטאנד בשבילכם 😎
ועכשיו הגיע הזמן ללכלך את הידיים, אנחנו נראה:
- פתיחה של פרויקט מעטפת – SHELL
- פתיחה של תת-פרויקט – MFE
- אירוח של הפרויקט על ידי ROUTE – LAZY LOADING
- אירוח של הפרויקט כרכיב על דף, ללא שינוי בראוט
- משיכה של מודול שלם עם מספר רכיבים, חילוץ של רכיב ספציפי והשמה שלו בדף
- העברה של מידע בין תתי-פרויקטים באופן ישיר
גירסאות מותקנות אצלי:
- NODE – 14.17 , היום יש את 16
- Angular – 13.3.3
- webpack – 5
בחלק האחרון של המדריך נדבר על העדכון האחרון שיצא ומה השינויים שבוצעו בו
נפתח 3 פרויקטים של אנגולר, רגילים לגמרי:
ng new shell ng new mfe1 ng new mfe2
נפתח כל אחד מהפרויקטים שנוצרו עם VS CODE.
בטרמינל של כל פרויקט נריץ את הפקודה הבאה בהטעמה לפרויקט
SHELL
ng add @angular-architects/module-federation --project shell --port 5000
MFE1
ng add @angular-architects/module-federation --project mfe1 --port 3000
MFE2
ng add @angular-architects/module-federation --project mfe2 --port 3001
שימו לב שבכל הרצה תשאלו אם לבצע התקנה, כמובן שהתשובה היא כן (Y)
אחרי הרצה של הפקודות, באופן רשמי אפשר להגיד שהפרויקטים שלנו מוכנים לעבודה במיקרו-פרונטאנד.
אחד הקבצים החשובים הוא webpack.config.js, שם כל הקסם נמצא 🧙♂️.
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); const mf = require("@angular-architects/module-federation/webpack"); const path = require("path"); const share = mf.share; const sharedMappings = new mf.SharedMappings(); sharedMappings.register( path.join(__dirname, 'tsconfig.json'), [/* mapped paths to share */]); module.exports = { output: { uniqueName: "mfe2", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, // For remotes (please adjust) name: "mfe2", filename: "mfe2.js", exposes: { './MainContentComponent': path.resolve(__dirname, './src/app/component/main-content/main-content.component.ts'), './SideContentComponent': path.resolve(__dirname, './src/app/component/side-content/side-content.component.ts'), }, shared: share({ "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, ...sharedMappings.getDescriptors() }) }), sharedMappings.getPlugin() ], };
בקבצים שנוצרו תשימו לב שיש שוני ממה שמוצג כאן, בכל קובץ יהיו לכם גם השורות עבור remotes וגם עבור hosts.
מה שלא רלוונטי אפשר פשוט למחוק.
שורה 2 : מגדירה שם יחודי עבור ההחצנה, ויש לשים לב שאין מיקרו פרויקטים בעלי אותו שם.
שורה 29: מגדירה את סוג ההחצנה, במקרה שלנו הברירת מחדל מתאימה – module, ניתן להגדיר גם lib ולטפל בהחצנה קצת אחרת.
שורה 33: שם הקובץ, אני ממליץ לשנות אותו ממשהו גנרי (remoteEntry) לשם שיעזור לנו לזהות מי זה הקובץ הזה ולאיזה פרויקט הוא שייך, לכן גם הוא שונה לשם הפרויקט
שורה 34 – 37: אם אנחנו נמצאים בפרויקט שמחצין רכיבים החוצה, זה האזור שעושה את זה. אנחנו יכולים לתת איזה שם שאנחנו רוצים כמפתח ובערך נרשום את המיקום של הרכיב.
אנחנו יכולים לכתוב מיקום סטטי וכנראה שלא נתקל ביותר מידי בעיות, אבל אם אנחנו ממש רוצים להיות רגועים אז אפשר לרשום את זה כמו שמוצג בשורות 35/36, כל מה שאנחנו אומרים לו זה לחבר את מיקום הספריה שאנחנו נמצאים בה להמשך היחסי שכתבנו, כך אנחנו יודעים שלא משנה איפה הפרויקט יהיה, המיקום תמיד יהיה נכון.
שימו לב, אצלכם בקובץ החלק הזה יהיה ריק או עם שורה אחת לדוגמה במצב הערה.
אין צורך להעתיק את מה שכתוב אצלי כרגע, אנחנו נגיע לזה בהמשך.
שורה 40 – 47: כאן אנחנו יכולים לשלוט בהתנהגות של קבצי העזר. ניתן לראות שכברירת מחדל כבר יש שם את כל הגרעין של אנגולר, כולם מסומנים כסינגלטון.
מה שזה נותן למערכת הוא הורדה של הקבצים האלו פעם אחת וזהו, במידה ויש מיספר MFE באנגולר, הקבצים האלו ירדו פעם אחת וכל שאר ה-MFE יעבירו רק את מה שכתוב בהחצנה ללא הקבצים הנוספים. כך שהם ישתמשו בקבצים שירדו בהתחלה ויחסכו תעבורה של קבצים מיותרים ויצירה של משתנים בזיכרון שיחזיקו את אותו המידע.
כמובן שאם יש לנו בעיות של הבדלי גרסאות, מהלך כזה יהיה בעיתי וצריך לשקול לבטל את השימוש החוזר וכן להוריד מספר גרסאות של אותו הקובץ.
בנוסף, אם המעטפת (SHELL) היא אנגולר, ואחד המיקרופרונטאנדים הוא ריאקט אז המערכת תוריד את כל הקבצים שנדרשים להפעיל אותו פעם אחת ובמידה שיהיו רכיבים נוספים של ריאקט לא יהיה צורך להוריד שוב את הקבצים האלו.
בחלק הבא נראה איך לטעון ב-lazyloading מבוסס ראוט מודול שלם מ-MFE