26

Our team is developing a large project and we want to build a big app with multiple forms and dashboards and features. One monolithic SPA would get complicated. So we discuss the approach of „micro frontend“ architecture. The goal is to generate a parent SPA which contains several child SPAs. All SPAs should use the same framework: vueJS.

Idea behind: Micro Frontends

  • a web app is a composition of features which are owned of independent teams
  • a team has a distinct area of business
  • the team is cross-functional and develops the features end-to-end from database to user-interface
  • it's like self-contained systems

We found some implementations supporting this:

  1. Micro Frontends
  2. Single-SPA of CanopyTax

We want to use VueJS in our frontend (vue, vue-router, vue-resource, vue-loade, webpack)

Questions

  1. Is a composite UI (i.e. Micro Frontend) feasible with VueJS (standard Vue-tools), are there example projects?
  2. We have more than one page, so we need a solution to navigate from one side to another. How can we realize page transitions?
  3. Is it possible to established an Event-Bus between the VueJS components?
  4. How can we implement a bidirectional communication between the components (parent-child, child-parent)?
hc_dev
  • 8,389
  • 1
  • 26
  • 38
René
  • 261
  • 1
  • 3
  • 3
  • 2
    Your question should be more on topic on Software Engineering I guess, since you do not show any code at all here. – Patrick Mevzek Mar 09 '18 at 15:16
  • 1
    The answer to all of these is yes. Vue is well suited to micro front end approach. You want to add vuex to your list of vue libraries to use and that would also take care of your Event-Bus. Bidirectional communication is done through passing props from parent to child and emitting events from child to parent. But you can use the bus for some of that as well. – skribe Mar 09 '18 at 18:35
  • 1
    HI I need same architecture for a big large app we are going to build in VueJS,so what you mean is that if I have 20 modules for example then better to create 20 VueJS apps (each by module? ) if this is true how do you share state management between them, or how do you use a component that is in other VueJS app from another VueJS app? All this sound really very interesting. Thanks – VAAA Aug 26 '18 at 13:23
  • About micro-frontends, you might consider mixing multiple front-end technologies (Vue, React, Web Components...) depending on what is best suited for the team / domain related to a micro-frontend. This is also explained [here](https://micro-frontends.org) – Rémi Bantos Nov 13 '18 at 13:20

2 Answers2

19

Feasibility: Composite UI

  1. Is it possible to create a composite UI (micro front end) based on vue by using standard vue tools?

Yes, it is possible. Pretty much any independent Vue-component you see published around (vue-select, vue-slider-component and even full "sets" of components such as vuetify, bootstrap-vue or vue-material are examples of reusable (composable) components developed using standard Vue tools.

Page transistions: Routing

  1. We have more than one page, so we need a solution to navigate from one side to another. How can we realize page transitions?

vue-router is the tool for this job. It is developed by the core team, so expect tight integration and great feature support.

Event-Bus

  1. Is it possible to established a Event-Bus between the VueJS components?

Every Vue instance implements an events interface. This means that to communicate between two Vue instances or components you can use Custom Events. You can also use Vuex (see below).

Bidirectional communication

  1. How can we implement a bidirectional communication between the components?

The best way to send data from parent component to child is using props. Steps:

  1. Declare props (array or object) in the child
  2. Pass it to the child via <child :name="variableOnParent">.

See demo below:

Vue.component('child-comp', {
  props: ['message'], // declare the props
  template: '<p>At child-comp, using props in the template: {{ message }}</p>',
  mounted: function () {
    console.log('The props are also available in JS:', this.message);
  }
})

new Vue({
  el: '#app',
  data: {
    variableAtParent: 'DATA FROM PARENT!'
  }
})
<script src="https://unpkg.com/vue@2.5.13/dist/vue.min.js"></script>

<div id="app">
  <p>At Parent: {{ variableAtParent }}</p>
  <child-comp :message="variableAtParent"></child-comp>
</div>

You can also get references for Child Components (refs) and call methods on them.

Vue.component('my-comp', {
  template: "#my-comp-template",
  props: ['name'],
  methods: {
    saveMyComp() {
      console.log('Saved:', this.name);
    }
  }
})

new Vue({
  el: '#app',
  data: {
    people: [{name: 'Bob'}, {name: 'Nelson'}, {name: 'Zed'}]
  },
  methods: {
    saveChild(index) {
      this.$refs.myComps[index].saveMyComp();
    }
  }
});
<script src="https://unpkg.com/vue@2.5.13/dist/vue.min.js"></script>

<div id="app">
  <div v-for="(person, index) in people">
    <button @click="saveChild(index)">saveMyComp</button>
    <my-comp :name="person.name" ref="myComps"></my-comp>
  </div>
</div>

<template id="my-comp-template">
    <span> {{ name }} </span>
</template>

To communicate from child to parent, you'll use events. See demo below. There are also several modifiers that make this task easier.

var parent = {
  template: '<div><child :content.sync="localContent"></child><br>At parent: {{ localContent }}</div>',
  props: ['content'],
  data() {
    return {
      localContent: this.content
    }
  }
};

var child = {
  template: '<div>At child: {{ content.value }}<button @click="change">change me</button></div>',
  props: ['content'],
  methods: {
    change() {
      this.$emit('update:content', {value: "Value changed !"})
    }
  }
};

Vue.component('child', child);
Vue.component('parent', parent);

new Vue({
  el: '#app'
});
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>

<div id="app">
  <parent :content="{value:'hello parent'}"></parent>
</div>

Vuex

Inevitably, though, as your application grows, you will have to use a more scalable approach. Vuex is the de facto solution in this case. Roughly, when using Vuex, you won't have to pass state around from parent to child: all of them will pick it up from the Vuex store (sort of a "global" reactive variable). This greatly simplifies the application management and is worth a post of its own.


Final note: As you can see, one great advantage of Vue is how easy you can prototype and test functionality. No build step, few abstractions over raw JS. Compared to other frameworks, I'd say this is an important bonus.

tony19
  • 125,647
  • 18
  • 229
  • 307
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • Great answer, happened to stop by , by mistake, although it was worth it!. – Jaya Mar 10 '18 at 01:36
  • @acdcjunior ... great work! Thank you for your detailed and well-founded answer. We have accepted your suggestions and have already tested them successfully. VueRouter and Vuex have proven to be very helpful. We will continue to follow the approach… – René Mar 20 '18 at 09:37
  • 4
    Though the OP may not have meant "child SPA", I am curious what the answer would be for that context. The context of this answer is really just about joining components, not SPAs. – dispake Jul 27 '19 at 01:12
  • Comprehensive answer, concretely demonstrating all points in question ⭐️ I suggested an edit to improve structure. – hc_dev Mar 23 '21 at 05:42
14

I have been curious looking for a quick way to implement the Micro-Frontend architecture. Some good resources I found are at:

  • micro-frontends.org: techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently.
  • Single-SPA.js: Javascript router for front-end microservices

However the problem I had with them is setup complexity. I like to see results pretty fast.

Piral

Piral.io (framework for portal-applications following microfrontends architecture) is still pretty much in development and it's mainly targeted for React (another JS frontend-framework).

Vue approach

I was able to come up with an approach and hope to write an medium article on that soon, however for the time being

  1. You can build each part of your application as an independent Web Component with Vue. A great place to start is Create & Publish Web Components With Vue CLI 3 - Vue.js Developers.
  2. You can use vue-router for page-transitions: with dynamic route matching (e.g /apps/:app_name) you can load sub-applications as appropriate. Within each sub-app, you can as well have a routing system in place
  3. As Event-Bus there's BroadcastChannel in recent browsers, which can be used to send messages across sub-apps. The Broadcast Channel API could be resourceful.
  4. BroadcastChannel can handle bi-directional communication.

Above approach works best if you want to:

  1. Have separate team use whatever tool they're most comfortable with.
  2. Add new apps, even in production without downtime.
hc_dev
  • 8,389
  • 1
  • 26
  • 38
amustapha
  • 416
  • 3
  • 12
  • 2
    I think this is the appropriate answer so far. – Ramon Gonzalez Aug 20 '19 at 16:53
  • I'd like to point people further to this read, where this microfrontend subject is pretty well explained: https://martinfowler.com/articles/micro-frontends.html – Ramon Gonzalez Aug 24 '19 at 17:57
  • https://piral.io is super cool - had an app shell running in minutes deployed it on an S3 bucket, set up a feed with https://feed.piral.io and deployed my first modules. All in a single run! I was fully using React though. – Florian Rappl Nov 09 '19 at 12:56
  • @amustapha - I am facing issue in my vue micro app with what u wrote in point # 2 i.e. You can use vue-router, and with dynamic route matching (e.g /apps/:app_name) you can load sub-applications as appropriate. Within each sub-app, you can as well have a routing system in place..Actually My vue web component using a vue router to manage its navigation and when i am using this web component in vue shell application which is also using its own vue router. it is not starting the shell app and throwing a error :Uncaught TypeError: Cannot redefine property: $router. What can i do here. thnx in adv. – Atul Feb 01 '20 at 16:55
  • @amustapha i also put my detail here https://stackoverflow.com/questions/60024245/micro-front-end-web-components-vue-router-error-uncaught-typeerror-cannot – Atul Feb 02 '20 at 08:08
  • Broadcast Channel is not supported by Safari, massive drawback here – petitkriket Apr 08 '21 at 17:14
  • Hey @petitkriket, I believe there's backward compatibility which falls back to localstorage on older browsers. I can't look into that right away, but this is something you can work with: https://www.npmjs.com/package/broadcastchannel-polyfill – amustapha Apr 19 '21 at 15:14