This document discusses patterns for implementing micro frontends with Vue. It describes Vue as a good framework for building micro frontends due to its flexibility. It then covers issues that can arise with micro frontends and provides solutions, such as using custom elements to ensure components are rendered correctly, passing data from server to client, and sharing components between micro frontends.
2. Who am I?
Albert Brand
Senior consultant @ Xebia
Pragmatic programmer
Proud husband & dad
twitter.com/al_bert_brand
medium.com/@albert.brand
linkedin.com/in/albertbrand
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 2
3. What is a micro frontend?
- Autonomously developed, tested and deployed frontend components
- Focused on a single business domain
- Implemented end-to-end by a team
- Offering more freedom and less dependencies for developers
- Very useful in a multi team setting
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 3
4. How can Vue help?
- Vue is an opinionated web framework
- Can build anything:
- large single page apps with client side routing; or
- a library of shared UI components; or even
- repackage as Web Components
- Easy to adjust to needs
- Not too much hard choices
Seems like a good match!
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 4
8. Frontend microservices
Browser Page
Server
App service
Micro
frontend
component
Micro
serviceAPI
HTML Proxy
Micro
service
Assets
HTML
Micro
frontend
component
Micro
serviceAPI
Micro
service
Assets
HTML
8
11. More server side concatenation tools
- Server side includes
- Compoxure
- Tailor
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 11
14. Example repository structure
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 14
/renderer
/templates
/domain-navigation
/backend
/assets
/templates
/frontend
/src
/domain-product
... same
/domain-cart
... same
15. Local development HTTP servers
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 15
/renderer <- serves at
http://localhost:8000
/domain-navigation /backend <- serves at
http://localhost:9000
/domain-product/backend <- serves at
http://localhost:9001
/domain-cart/backend <- serves at
http://localhost:9002
16. Example page render GET request
- Renders a template with menu, cart, product and footer component
- Queries micro frontend endpoints
- Can be queried in parallel, slowest is bottleneck
- Need to handle failing micro frontend endpoint
- No / short cache period for endpoint result
- Returns rendered HTML page
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 16
21. Run build
/domain-navigation/frontend$ npm run build
⠋ Building for production…
Build complete. The ../backend/assets directory is ready to be
deployed.
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 21
26. Issues
- Components are only rendered once
- How to send server side data to client side?
- How to call authenticated microservice APIs?
- Everybody's touching my browser's globals
- How do we share components?
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 26
27. Components are only rendered once
Instead of looking at IDs, use custom elements:
<example-menu></example-menu>
<example-product-detail></example-product-detail>
<example-cart></example-cart>
<example-footer></example-footer>
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 27
28. Components are only rendered once
Then add a loop in each entrypoint:
const components = {
"example-menu": Menu,
"example-footer": Footer,
};
Object.entries(components).forEach(([tagName, Component]) => {
document.getElementsByTagName(tagName).forEach(el => {
new Vue({
el,
render: h => h(Component)
});
});
}); Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 28
29. How to send server side data to client side?
Continuing on the custom elements solution:
<example-menu active="home"></example-menu>
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 29
30. How to send server side data to client side?
Update the entrypoint:
Object.entries(components).forEach(([tagName, Component]) => {
document.getElementsByTagName(tagName).forEach(el => {
const props = Array.from(el.attributes).reduce((props, attr) => {
props[attr.nodeName] = attr.nodeValue;
return props;
}, {});
new Vue({
el,
render: h => h(Component, { props })
});
});
});
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 30
31. How to send server side data to client side?
Alternative solution: treat pieces of document structure as Vue template.
Enable the Vue runtime compiler in vue.config.js:
module.exports = {
...
runtimeCompiler: true,
}
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 31
32. How to send server side data to client side?
The template should be adjusted:
<example-menu class="navigation" active="home"></example-menu>
<example-product-detail class="product"></example-product-detail>
<example-cart class="cart"></example-cart>
<example-footer class="navigation"></example-footer>
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 32
33. How to send server side data to client side?
Update the entrypoint:
const components = {
"example-menu": Menu,
"example-footer": Footer,
};
document.getElementsByClassName("menu").forEach(el => {
new Vue({
el,
components,
});
});
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 33
34. How to send server side data to client side?
Bonus: this makes component composition possible:
<example-menu class="navigation" active="home">
<example-menu-item href="/">Home</example-menu-item>
<example-menu-item href="/shop">Shop</example-menu-item>
</example-menu>
Downside is extra 10kb for the runtime compiler in each bundle.
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 34
35. How to call authenticated microservice APIs?
You want to use a prope web token auth framework such as OAuth.
When that's in place you have basically two options:
- store the token on the client and pass it along proxy calls
- store the token on the server and use a session cookie on the client
The last option requires you to tinker with your proxy and make it session-aware.
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 35
36. Everybody's touching my browser's globals
Rules for all teams:
- Only touch your own document structure
- No pollution of global JS scope (should be fine if using Webpack)
- Regulate usage of web APIs (for instance, only 1 client side router allowed)
- If needed, communicate on a page via browser events
- Prefix CSS and limit to document structure
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 36
38. Browser's globals: CSS
Use a Vue preprocessor to prefix third party CSS:
.navigation {
@import 'buefy.css'
}
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 38
39. How do we share frontend components?
1. Create a Vue component library.
2. Share using a private NPM repo.
3. Include the components that you use in your bundle.
There are some advanced Webpack tricks to reduce bundle size! For next time...
Vue micro frontend implementation patterns - Xebia - @al_bert_brand - 39
Frontend app / app service are still a monolith
You can choose to expose microservice APIs but that will probably bite you.
Proxy can also be a generic gateway or own deployment as you see fit
App service = app shell
The HTML and assets microservice is often called the Backend-for-Frontend
If you squint your eyes this is the high level architecture. A domain should be the focus of a single team.
You probably can! But:- Bad SEO- Load issues- Layout issues- Accessibility issues
Do some research to see what is a good fit (or roll your own of course).
Let's dive into a basic example
Can be single repository or split per domain over multiple repo's. Split will help with autonomy.
We are focusing on the micro frontend part, so I left out the API microservices.
In the end, these folders will be packaged and deployed autonomously on some infrastructure.
I left that out for your listening pleasure, we're not diving into Docker, Kubernetes and Terraform this time :).
What is queried:
retrieve server side rendered HTML fragment for each micro frontend component (this could return a SSR web component)
retrieve JS and CSS frontend asset locations that need to be directly loaded (to enable client side behavior and separated styles)
Failing endpoint:
Short timeout, else user experience suffers
Add circuit breaker
Caching:
Sane redeployment
This assumes that there are exactly 2 component tags on the page. No worries, we will revisit this soon.
The HtmlWebpackPlugin renders embedded Javascript templates, which is not limited to HTML output.
We can use it to render the frontend assets JSON file as it has access to the bundled CSS and JS filepaths.Alternative is to build your own plugin.
Writes all bundled / optimized assets in the backend folder. You can make this a part of a containerised build as you see fit.
Hey, it's a Go template. Learn a new language every year.
By not providing a render function, Vue treats the specified `el` as template. This essentially triggers rendering all recognised Vue elements in the element branches marked with the class.