
Angular, React, Vue.Js and Co.
Peacefully United thanks to Micro Apps and Web Components
Table of Contents
This blog post is part of an article series about Micro Apps:
- A Software Architect’s Approach Towards Using Angular (And SPAs In General) For Microservices Aka Microfrontends
- Micro Apps With Web Components Using Angular Elements
- Angular, React, Vue.Js and Co. peacefully united thanks to Micro Apps and Web Components
Related series about Web Components with Angular Elements:
In my article Micro Apps With Web Components Using Angular Elements I’m showing how to leverage Angular Elements and Web Components to implement a modern Micro Apps architecture. It shows a shell that loads individual Micro Apps that can be developed as well as deployed separately. Those Micro Apps are provided as Web Components and loaded dynamically at runtime.
In this article I’m going one step further: I’m using different technologies for different micro apps.
Of course, you don’t do something like this just for fun. It’s rather necessary if your application evolves for a long time like one decade or more and if you have several UI teams. In this case, each team can go with the “best” technology for their part and you have not to stick with your original technology decision for ten years or longer.
Case Study
To show how to mix and match technologies, I’m using an extend version of the case study from my last post:
Here, you see a shell application with an Angular-based Micro App containing a Vue-based and a VanillaJS-based widget.
You can also mix and match technologies at macro-level for providing different micro apps:
Besides this, each Micro App can also run standalone:
This is important because it allows for separate development, testing and deployment or to put it in another way: You are minimizing dependencies between different UI teams.
Wrapping a Micro App in a Web Component
For wrapping the Angular parts of my example in Web Components, I’m using Angular Elements. Further information about this can be found in my article here.
For the Vue.JS part, I’m directly using the native Custom Elements API. As an alternative, you could also use the build-in option for providing Web Components which is available since version 3. However, while it seems to be a good fit for widgets, I felt I needed more flexibility for wrapping a whole micro app which also uses the router or other libraries.
The next listing shows how to use the Custom Elements API for wrapping the Micro App:
import Vue from 'vue'
import Booking from '../components/Booking.vue'
import initRouter from '../initRouter.js';
import store from '../SimpleStore.js';
export default class FlightBooking extends HTMLElement {
get appState() {
return this._appState;
}
set appState(value) {
this._appState = value;
this.vue.$data.appState = value;
}
static get observedAttributes() {
return ['app-state'];
}
attributeChangedCallback(name, oldValue, newValue) {
this.appState = JSON.parse(newValue);
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.render();
}
render() {
const cssBase = require('!to-string-loader!css-loader!../assets/css/bootstrap.min.css');
const cssTheme = require('!to-string-loader!css-loader!../assets/css/paper-dashboard.css')
this.shadowRoot.innerHTML = `
<style>${cssBase}</style>
<style>${cssTheme}</style>
<div id="component"></div>
`;
const router = initRouter();
const handleMessageEvent = (msg) => {
this.dispatchEvent(new CustomEvent('message', { detail: msg }));
}
this.vue = new Vue({
router,
data: {
store,
appState: this.appState
},
render(r) {
return r(Booking, {
props: {
appState: this.appState
},
on: {
message: handleMessageEvent
}
});
}
});
const comp = this.shadowRoot.getElementById('component');
this.vue.$mount(comp);
}
}
The Web Component is just a subclass of HtmlElement
. For the communication with the shell, all my Micro Apps come with an appState
property and a message
event. For instance, the appState
contains the passenger and flight in question and the message event informs the system when a flight has been booked.
I’m also syncing the app-state
attribute with the appState
property. Every time the appState
changes, I’m passing it to the current Vue object.
By means of attachShadow
I’m using Shadow DOM which isolates the component’s layout from the rest of the application. Simply spoken, this prevents a global CSS from destroying the component’s layout.
The render
method displays the component by leveraging Vue. To load some CSS instructions just for the component, it creates an own style tag. The CSS itself is loaded using webpack’s to-string-loader
and css-loader
. They have to be npm-installed separately.
The Vue object gets a configured router, a data object with values for the component (e. g. the appState
) and a render
method.
The latter one is a bit confusing. If you would rewrite it using a html element with data binding expressions it would look like this:
<booking :app-state="appState" @message="handleMessageEvent"></booking>
For using such a template, we’d need the Vue template compiler at runtime.
However, to get the best performance, it’s a best practice to precompile Vue-templates. Hence, we don’t need not to put the Vue compiler into our bundles.
While the Vue CLI automatically precompiles all template in vue files we have to provide this render function in our wrapping Web Component instead of the template to get rid of the compiler at runtime.
Registering Web Components
To register this web component alongside another one wrapping the basked shown above, we just call customElements.define
in the application’s entry point which is normally called main.js
:
import Vue from 'vue'
import FlightBookingCE from './custom-elements/FlightBookingCE.js'
import FlightBasketCE from './custom-elements/FlightBasketCE.js'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
customElements.define('flight-booking', FlightBookingCE);
customElements.define('flight-basket', FlightBasketCE);
We can also use this entry point to register plugins like the router.
To get the bundles, just call the VUE.js CLI: vue-cli-service build
.
This command gives us two bundles we need to load together. To load them, we can dynamically create script tags and tags for the web components as shown here.
Summary
Micro Apps and Web Components allow for mixing different technologies. This can be important for applications developed over a long time and/or by different UI teams. You can use the “best” technology for each part of the application and you don’t need to stick with your technology decision forever.
Unsere Angular-Schulungen
