diff --git a/content/english/blog/frontend/module-federation.md b/content/english/blog/frontend/module-federation.md new file mode 100644 index 0000000..b8c567e --- /dev/null +++ b/content/english/blog/frontend/module-federation.md @@ -0,0 +1,143 @@ +--- +title: "Micro-frontend using Module federation" +meta_title: "" +description: "Micro-frontend using module federation with vite and docker" +date: 2024-02-27T05:00:00Z +categories: ["frontend", "react", "vite"] +author: "Hugo Sjoberg" +tags: ["frontend", "react", "vite"] +draft: false +--- + +# Micro-frontend + +Micro frontends is an architectural used to break down a big website into smaller frontends. Imagine you work for a big-ass company and you have one team responsible for the navbar, one team responsible for the footer etc. + +{{< notice "warning" >}} +This is why you stay away from big companies! +{{< /notice >}} + +It can also be useful if you have several static websites which you would like to run next to each other, sharing things such as login but one website is using react-12 and the other website is running the newest a shiniest version of react. + +Let's see how we can do this using vite and deploy everything in a docker container(This kind of defeats the purpose of being able to deploy different components independently ¯\\_(ツ)_/¯ ) + +# Host + +Let's start with the host-app, this will be the main app pulling in the other remote app(s). + +```bash +npm create vite@latest +``` + +Replace the `vite.config.ts` with the following: + +```ts +import { defineConfig, loadEnv } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import federation from '@originjs/vite-plugin-federation'; + +interface Imodules { + remote: string; +} + +export default ({ mode }) => { + process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }; + const modules: Imodules = { + remote: '/remote/assets/remoteEntry.js', + }; + if (mode === 'development') { + modules.remote = `http://localhost:3002${modules.remote}`; + } + return defineConfig({ + plugins: [ + react(), + federation({ + name: 'host-app', + remotes: { + remote: modules.remote, + }, + shared: ['react'], + }), + ], + build: { + target: 'esnext', + }, + }); +}; +``` + +The `modules.remote` will tell the host-app where to find the remote app. + +In your `app.tsx` add the following line to import the remote-app: + +```ts +const RemoteApp = lazy(() => import('remote/Remote')); +``` + +# Remote + +In your remote app, update your `vite.config.ts` to look something like this: + +```ts +import { defineConfig, loadEnv } from 'vite'; + +import federation from '@originjs/vite-plugin-federation'; +import react from '@vitejs/plugin-react-swc'; + +export default ({ mode }) => { + process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }; + const base = process.env.DEV ? '' : '/remote'; + return defineConfig({ + plugins: [ + react(), + federation({ + name: 'remote-app', + filename: 'remoteEntry.js', + // Modules to expose + exposes: { + './Remote': './src/app.tsx', + }, + shared: ['react'], + }), + ], + base: base, + build: { + target: 'esnext', + }, + }); +}; +``` + +Here we specify what to expose (`app.tsx`) + +# Run everything together + +To run everything together I like to use docker, here is an example [Dockerfile](https://github.com/hugosjoberg/blog-code/blob/main/vite-module-federation/Dockerfile) I used. + +In the Dockerfile I use nginx as a reverse-proxy to serve the static content, my `nginx.conf` file: + +```conf +server { + listen 3000; + + location / { + root /usr/share/nginx/html/host; + include /etc/nginx/mime.types; + try_files $uri $uri/ /index.html; + } + + location /remote/assets/ { + alias /usr/share/nginx/html/remote/assets/; + include /etc/nginx/mime.types; + try_files $uri $uri/ /index.html; + } +} +``` + +The `/` path is pointing to my host app and `/remote/assets/` is pointing to my remote app. When you hit `/` the host app will tell your browser to fetch data over at `/remote/assets/`. + +And just like that we have created a trendy micro frontend. + +# Code + +All the code can be found [here](https://github.com/hugosjoberg/blog-code/tree/main/vite-module-federation) diff --git a/hugo_stats.json b/hugo_stats.json index d6a3ed3..99f0b87 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -60,7 +60,6 @@ "video" ], "classes": [ - "...", "2xl:bg-pink-200", "2xl:block", "2xl:hidden", @@ -86,14 +85,8 @@ "btn-sm", "capitalize", "col-12", - "col-span-1", "col-span-2", - "col-span-4", - "col-span-5", "col-span-6", - "col-span-8", - "col-start-2", - "column", "container", "content", "cursor-pointer", @@ -133,13 +126,9 @@ "gallery", "gallery-item", "gallery-slider", - "gap-4", "glightbox", "grid", "grid-cols-10", - "grid-cols-5", - "grid-cols-6", - "grid-container", "gx-5", "h-6", "h2", @@ -307,7 +296,6 @@ "top-0", "top-[4px]", "uppercase", - "w-5/12", "w-[30px]", "w-full", "warning", @@ -348,12 +336,14 @@ "heading-5", "heading-6", "hide-button", + "host", "i-wanna-talk-about-the-assassination-attempt", "image", "implement-the-interface", "learning-and-growth", "link", "message", + "micro-frontend", "name", "nav-menu", "nav-toggle", @@ -365,7 +355,9 @@ "project-structure", "protection-of-personal--information", "real-world-example", + "remote", "responsibility-of-contributors", + "run-everything-together", "search-modal-input", "show-button", "slider", diff --git a/resources/_gen/assets/css/css/style.css_82b067fd6c564693170dff5ae4660239.content b/resources/_gen/assets/css/css/style.css_82b067fd6c564693170dff5ae4660239.content index b36532d..89fe8f7 100644 --- a/resources/_gen/assets/css/css/style.css_82b067fd6c564693170dff5ae4660239.content +++ b/resources/_gen/assets/css/css/style.css_82b067fd6c564693170dff5ae4660239.content @@ -11208,27 +11208,12 @@ input#nav-toggle:checked ~ #nav-menu { .order-3 { order: 3; } -.col-span-1 { - grid-column: span 1 / span 1; -} .col-span-2 { grid-column: span 2 / span 2; } -.col-span-4 { - grid-column: span 4 / span 4; -} -.col-span-5 { - grid-column: span 5 / span 5; -} .col-span-6 { grid-column: span 6 / span 6; } -.col-span-8 { - grid-column: span 8 / span 8; -} -.col-start-2 { - grid-column-start: 2; -} .m-1 { margin: 0.25rem; } @@ -11326,9 +11311,6 @@ input#nav-toggle:checked ~ #nav-menu { .h-6 { height: 1.5rem; } -.w-5\/12 { - width: 41.666667%; -} .w-\[30px\] { width: 30px; } @@ -11344,12 +11326,6 @@ input#nav-toggle:checked ~ #nav-menu { .grid-cols-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } -.grid-cols-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); -} -.grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); -} .items-start { align-items: flex-start; } @@ -11362,9 +11338,6 @@ input#nav-toggle:checked ~ #nav-menu { .justify-between { justify-content: space-between; } -.gap-4 { - gap: 1rem; -} .space-x-1 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.25rem * var(--tw-space-x-reverse));