Micro-Frontends

Micro-Frontends

Micro-frontends are what I like to call "Web Components without Web Components." They solve the same problem: "How can I write front-end code but keep things organized and have functions appropriately scoped?"

Micro-frontends, as an architectural style, inspire a sense of autonomy and independence. They break down a monolithic front-end into smaller, manageable pieces, drawing inspiration from micro-services. Each part of the application can be independently developed, deployed, and maintained, offering a world of potential for individual growth and innovation.

In a perfect world, you would have a team dedicated to one specific "vertical slice". To illustrate this, we will have an About Page and Home Page. We would have a team dedicated to all things related to each one. Which means they would own the CSS, the HTML and the Javascript code for it.

When you consider it, it makes perfect sense because you can divide a web application into distinct slices that are owned by different teams, allowing for autonomous development and deployment.

There are people who disagree and think that it is healthier to think about libraries rather than services for front-ends. Component libraries offer much more "bang for the buck" than going with a micro-frontend in the long run.

On the other hand, I think micro-frontends are a fantastic solution that tickles my roots as a back-end developer. They allow you to effectively utilize SoC (Separation of Concerns) in a web development environment.

I know it is all the rage to say that Object-Oriented Programming and SoC are bad, but anyone touting garbage like that is a bad developer who doesn't understand the topic they are talking about.

Enough with the preamble, let's get to why you are reading this article. The code.

Our project structure will look as follows:

micro-frontend-example/

├── index.html

├── main.js

├── styles/

│ ├── style.css

├── header/

│ ├── index.html

├── footer/

│ ├── index.html

├── content/

│ ├── home.html

│ ├── home.js

│ ├── home.css

│ ├── about.html

│ ├── about.css

│ ├── about.js

│ └── index.html


Let's start off with the index.html file at the root of the directory.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Micro-Frontends Example</title>
    <link rel="stylesheet" href="styles/style.css">
</head>
<body>

<div id="header"></div>
<div id="content"></div>
<div id="footer"></div>
<script src="main.js"></script>

</body>
</html>        

Nothing too crazy here, just a standard html file which has a reference to our main.js and style.css file along with a couple divs with ids, pretty standard stuff.

document.addEventListener("DOMContentLoaded", () => {
    loadComponent('header/index.html', 'header');
    loadComponent('footer/index.html', 'footer');
    loadPage('home');

    document.addEventListener('click', (event) => {
        if (event.target.tagName === 'A' && (event.target.getAttribute('href') === 'home' || event.target.getAttribute('href') === 'about')) {
            event.preventDefault();
            const page = event.target.getAttribute('href');
            loadPage(page);
        }
    });
});

function loadStylesheet(url) {
    const existingLink = document.querySelector(`link[href="${url}"]`);
    if (existingLink) {
        existingLink.remove();
    }

    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = url;
    link.onload = () => console.log(`Loaded stylesheet: ${url}`);
    link.onerror = () => console.error(`Error loading stylesheet: ${url}`);
    document.head.appendChild(link);
}

function loadComponent(url, elementId) {
    fetch(url)
        .then(response => response.text())
        .then(data => document.getElementById(elementId).innerHTML = data)
        .catch(error => console.error('Error loading component:', error));
}

function loadPage(page) {
    const contentDiv = document.getElementById('content');
    fetch(`content/${page}.html`)
        .then(response => response.text())
        .then(data => {
            contentDiv.innerHTML = data;
            loadStylesheet(`content/${page}.css`);
            loadScript(`content/${page}.js`);
        })
        .catch(error => console.error('Error loading page:', error));
}

function loadScript(url) {
    const existingScript = document.querySelector(`script[src="${url}"]`);
    if (existingScript) {
        existingScript.remove();
    }

    const script = document.createElement('script');
    script.src = url;
    script.onload = () => console.log(`Loaded script: ${url}`);
    script.onerror = () => console.error(`Error loading script: ${url}`);
    document.head.appendChild(script);
}        

The main.js file is also nothing special. A load component, load stylesheet, load page, load script function which removes repeated usage of the fetch function for multiple items. We also have button click event handlers defined. Pretty par for the course stuff here.

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
}
header, footer {
    background-color: #f8f8f8;
    padding: 1em;
    text-align: center;
}        

The css file is pretty basic as well. We can move on to the header directory's index.html file

<header>
    <h1>Micro-Frontend Example</h1>
    <nav>
        <a href="home">Home</a>
        <a href="about">About</a>
    </nav>
</header>        

The HTML looks a bit different here as we don't have the initializing boilerplate. What's going on?

To put it simply, we are injecting this code into the main index.html file. Which means that our header, footer and the content we want to display is self contained and called only when needed.

This is where the magic and power from micro-frontends come into play.

Let's look at the footer's index.html

<div class="footer">
    <p>&copy; 2024 Micro-Frontends Example. All rights reserved.</p>
</div>        

The same thing here. Let's move on to the fun part, which is the content section where we have some new additions.

We should probably begin with the content's index.html file as it is the final item without deviations to the pattern so far.

<div class="content">
    <p>Welcome to the micro-frontends example application. Click the links above to navigate.</p>
</div>        

Now for the fun part, the home html file

<h2>Home Page</h2>
<p>Welcome to the home page!</p>        

"Wait... I thought you said this was going to be different?", on the surface, this is the same thing, but, we have an addition, the home.js file.

document.addEventListener('DOMContentLoaded', () => {
    console.log('Home page script loaded');
    // Add any additional JavaScript specific to the home page here
});        

This code will fire whenever the home page is loaded. I know, basic but gets the point across.

But wait, there's more... How about css for the home page only?

/* Styles specific to the home page */
h2 {
    color: blue;
}
p {
    font-size: 1.2em;
}        

We will do this same thing for the about html and js files

<h2>About Page</h2>
<p>This is the about page.</p>        
document.addEventListener('DOMContentLoaded', () => {
    console.log('About page script loaded');
    // Add any additional JavaScript specific to the about page here
});        

Now, for the self contained css file for the about page.

/* Styles specific to the about page */
h2 {
    color: green;
}
p {
    font-size: 1.2em;
}        

This is obviously a very simple example but I would be more than happy to create a more fully fledged website example of this, perhaps something like a portfolio site or recreating my website (gamedevmadeeasy.com)

A link to the repository can be found at:


I hope you enjoyed this ad-hoc article.






要查看或添加评论,请登录

Jesse Glover的更多文章

  • Comprehensive AI and Decision-Making Systems Categories

    Comprehensive AI and Decision-Making Systems Categories

    State-Based Systems Finite State Machines (FSM) Hierarchical State Machines (HSM) State Pattern Pushdown Automata Timed…

  • Adventures in Learning

    Adventures in Learning

    As I’ve grown as a developer, I constantly ask myself questions like, “How would I create x?” This is a rabbit hole…

  • Visual Scripting Design Proposal

    Visual Scripting Design Proposal

    Link to the document on my googledrive https://drive.google.

  • Unity3D Development Courses on GameDevAcademy

    Unity3D Development Courses on GameDevAcademy

    2 of the 4 tutorials I have written for Zenva corp are now live and readable. The next two which focus on the…

社区洞察

其他会员也浏览了