Kombinieren von Front-End-Frameworks/Bibliotheken mittels Modul Federation

Kombinieren von Front-End-Frameworks/Bibliotheken mittels Modul Federation
10/7/2020

Ich denke, fast jeder hat von dem neuen Killer-Feature mit dem Namen Modul 'Federation' gehört. Meiner Meinung nach, können wir mit dieser Funktion entweder bereits geschriebene Open-Source-Module verwenden und sie in einer App zusammenführen, oder sie kann für ein großes Projekt nützlich sein, bei dem verschiedene Teams unabhängige Module erstellen können. Am wichtigsten ist, dass alle Beispiele nur in einem einzigen Ökosystem dargestellt werden (React-Bibliothek).

Nachdem ich diesen Artikel über Mikro-Front-Ends gelesen hatte, beschloss ich, eine Mikro-Front-End-Anwendung mit dem Modul 'Federation' zu schreiben.

Meine erste Absicht war es, React und Svelte zu kombinieren. Aber am Ende hatte ich mit Problemen mit dem Svelte-Loader zu kämpfen und fand nur eine Lösung, um Aliase und Hauptfelder für die Webpack-Konfiguration bereitzustellen. Leider funktionierte diese Lösung in meinem Fall nicht (Github, Stack overflow). Daher habe ich diesen Weg verlassen und mich für Vue entschieden.

Miteinander kombinieren

Als Entwickler haben Sie grundsätzlich zwei Möglichkeiten:

  1. Zugriffsmodul aus Window-Objekt
  2. Modul importieren und dem DOM-Element zuweisen

Zugriff auf Modul vom Window Objekt aus

Der Zugriff über die Window Objekt Eigenschaften kann einfach durch Zugriff auf den Bibliotheksnamen erfolgen. Dieser ist im ModuleFederationPlugin definiert. Zum Beispiel folgendermaßen:

src: modules/vue-module/webpack.config.js

{% c-block language="js" %}
new  ModuleFederationPlugin({
   name: 'vue',
   library: { type:  "var", name:  "vue" },
   exposes: {App: './src/bootstrap'}
   ...etc
})
{% c-block-end %}

Dann können wir auf das Modul zugreifen.

src: modules/main/public/index.html

{% c-block language="js" %}
window.vue.get('App').then(module => {
const vueInstance = module().default;
vueInstance.$mount('#vue');
});
{% c-block-end %}

Im Grunde genommen haben wir einen asynchronen Import, aus dem wir unsere Vue-Instanz über den Standardexport nehmen und in ein Element im DOM mounten können.

Importieren Sie das Modul innerhalb der React Komponente und weisen Sie es dem DOM-Element zu

Kommen wir nun zum angenehmen Teil

Zur Montage der Vue-Komponente, die benötigt werden:

  1. useRef:
    Um das Child anzuhängen, in das wir die Vue-Instanz einbinden werden
  2. useEffect:
    Zur Handhabung des Lebenszyklus der React Komponente und zur Bereitstellung von Side Effects
  3. document.createElement:
    Für den Mount von Vue-Instanzen <- dieser explizit definierte Container muss bereitgestellt werden, da Vue beim Mount den Container für das Vorlagen-Rendering verwendet.

Das sieht in etwa so aus: src: modules/main/src/VueContainer.jsx

{% c-block language="js" %}
import React, { useRef, useEffect } from 'react'
import app from 'vue/App';
const container = document.createElement('div');
function VueContainer() {
       const ref = useRef();
       useEffect(() => {
           const currentElement = ref.current;
           const vueInstance = app();
           if(currentElement) {
                  currentElement.appendChild(container);
                  vueInstance.$mount(container);
           }
           return () => {
                  vueInstance.$destroy();
                  container.remove();
           }
       }, [ref])
       return <div ref={ref}></div>
}
{% c-block-end %}

Es funktioniert, aber…

Darstellungsprobleme

Wir erhalten  die Vue-Instanz, aber das Template enthält keine Eigenschaften. Statt einer gerenderten Komponente erhalten wir eine Reihe von Fehlern, bei denen Eigenschaften im Template definiert sind, die Instanz aber nicht über diese verfügt. Nach mehrstündigem Suchen fand ich einen großartigen Artikel über Render-Funktionen in Vue. (Ich möchte darauf hinweisen, dass ich eine Art Neuling in Vue bin. Wenn Sie also bessere Lösungen kennen, lassen Sie es mich bitte wissen). Wir müssen die Standard-Renderfunktion außer Kraft setzen und die Vorlagen, die wir rendern wollen, explizit bereitstellen. Dazu erstellen wir eine Komponente mit der Funktion Vue.component, mit einem inneren Zustand, den wir verwenden werden. Das andere Problem ist das erneute Rendern. Wenn die Vue-Instanz einmal gemountet ist und die React-Komponente nicht gemountet ist, dann wird auch die Vue-Komponente entfernt und nicht neu gerendert. Daher sollte ein erzwungenes erneutes Rendering vorgesehen werden. Ich habe noch keine bessere Lösung gefunden, außer der Erstellung einer Funktion, die eine Vue-Instanz erzeugt - auf jedem Mount einer React-Komponente wird eine neue Instanz erstellt und an den Container gemountet.

In etwa so:
src: modules/vue-module/src/bootstrap.js

{% c-block language="js" %}
const app = () => new Vue({
   render: function(h) {
   const renderTemplate = this._c || h;
   return renderTemplate(App); // <- App.vue
   }
});
{% c-block-end %}

Aber dieser Ansatz hat auch seine Schwächen - mit jedem Rendern verlieren die Vue-Komponenten ihren Zustand.

Ein paar Worte zum Schluss

Dies war ein einfaches Sandbox-Beispiel für die Kombination verschiedener Frameworks und Bibliotheken. Ich bin mir nicht sicher, ob dies für Produktionszwecke verwendet werden kann, aber es ist eine coole Funktion, die Spaß macht und mit der man spielen kann, und ich möchte sie in Zukunft noch etwas genauer untersuchen.  

Link zum Code-Beispiel

Was steht auf meiner To-Do-Liste?

  • Kombination mit anderen Frameworks/Bibliotheken
  • Eine Lösung für einen persistenten Zustand finden
Teilen:
Radmir ist Web- und Mobil-Apps Entwickler. Seine berufliche Karriere begann 2018, aber er hat sich schon immer für das Erlernen neuer Technologien interessiert. Die meiste Erfahrung hat er in Java/Typescript, React, MobX und Express.js gesammelt. Er liebt Musik und Computerspiele, jeweils alle Genre.

Article collaborators

SABO Mobile IT

Für unsere Kunden aus der Industrie entwickeln wir spezialisierte Software zur Umsetzung von Industry 4.0. IoT, Machine Learning und Künstliche Intelligenz ermöglichen uns, signifikante Effizienzsteigerungen bei unseren Kunden zu erzielen.
Über uns