2017-06-27 9 views
4

Ich denke über den Aufbau einer Webanwendung, wo Menschen Plugins installieren können. Ich möchte, dass Plugins React-Komponenten definieren können, die auf der Seite gerendert werden, ohne das Haupt-JavaScript-Paket nach der Installation neu kompilieren zu müssen.Dynamisch laden Reagieren Komponenten

hier ist also der Ansatz, den ich von denke:

  • Bundle das Haupt JavaScript mit Reagieren als external library, mit webpack.
  • Lassen Sie Plugin-Autoren ihre Komponenten mit React als externe Bibliothek kompilieren.

Auf diese Weise ich nur eine Instanz von React ausführen. Ich könnte wahrscheinlich dasselbe mit einigen anderen häufig benutzten Bibliotheken tun.

Das Problem ist dann, wie diese Plugin-Komponenten vom Server dynamisch geladen werden. Lassen Sie uns sagen, dass ich die folgende Komponente:

class PluginRenderer extends React.Component{ 
    componentWillMount() { 
    getPluginComponent(`/plugins/${this.props.plugin}/component.js`).then((com) => { 
     this.setState({pluginComponent: com}); 
    }) 
    } 

    render() { 
    var Plugin = this.state.pluginComponent; 
    return Plugin ? <Plugin {...this.props} /> : "Loading..." 
    } 
} 

Wie getPluginComponent umgesetzt werden könnten?

+0

Um dies zu erreichen, benötigt man serverseitiges Rendering mit NodeJS um auf das Dateisystem zuzugreifen und ich bin mir immer noch nicht sicher ob es möglich wäre, ohne dass die Komponente irgendwie von Babel kompiliert werden müsste das passiert nicht zur Laufzeit. – Foxhoundn

+0

Ja, ich weiß, dass die Komponente noch kompiliert werden muss. Die kompilierte Quelle würde als statisches Asset dienen. – bigblind

Antwort

4

Es ist ein interessantes Problem, vor dem ich vor einigen Monaten für die Kundenarbeit stand, und ich habe nicht zu viele Dokumentansätze gesehen. Was wir getan haben ist:

  1. Individuelle Plugins getrennte Webpack Projekte sein wird, für die wir entweder eine Vorlage oder ein CLI-Tool zur Verfügung stellen, die Projektvorlagen erzeugt.

  2. In diesem Projekt definieren wir Webpack externals für gemeinsam genutzte Bibliotheken Anbieter bereits in der Kernanwendung verwendet: Reagieren, Redux usw. Dies teilt das Plugin nicht die in dem Bündel enthält, sondern sie in window von einer Variablen zu packen wir in der Kern-App festgelegt. Ich weiß, klingt wie saugt, aber es ist viel besser, als alle Plugins Tausende von geteilten Modulen wieder aufzunehmen.

  3. Unter Verwendung dieses Konzepts von external bietet die Core-App auch einige Dienste über Fensterobjekte für Plugins. Am wichtigsten ist eine PluginService.register() Methode, die Ihr Plugin aufrufen muss, wenn es initialisiert wird. Wir invertieren die Kontrolle hier: das Plugin ist verantwortlich zu sagen "Hallo, ich bin hier, das ist mein Hauptexport (die Komponente, wenn es ein UI-Plugin ist)" zur Kernanwendung.

  4. Die Kern-Anwendung hat eine PluginCache-Klasse/Modul, die einfach einen Cache für geladene Plugins hält (pluginId -> was auch immer das Plugin exportiert, Fn, Klasse, was auch immer). Wenn ein Code zum Rendern ein Plugin benötigt, fragt er diesen Cache danach. Dies hat den Vorteil, dass eine <Loading /> oder <Error /> Komponente zurückgegeben werden kann, wenn ein Plugin nicht korrekt geladen wurde, und so weiter.

  5. Zum Laden von Plugins lädt dieser PluginService/Manager die Plugin-Konfiguration (welche Plugins sollte ich laden?) Und erstellt dann dynamisch injizierte script Tags, um jedes Plugin-Bundle zu laden. Wenn das Paket fertig ist, wird der in Schritt 3 beschriebene Aufruf register aufgerufen, und Ihr Cache in Schritt 4 enthält die Komponente.

  6. Anstatt zu versuchen, das Plugin direkt von Ihrer Komponente zu laden, fragen Sie danach im Cache nach.

Dieses eine sehr hohe Level-Übersicht ist die so ziemlich auf unsere Anforderungen dann wieder gebunden ist (es war ein Armaturenbrett ähnliche Anwendung, wo Benutzer können Panels on the fly hinzufügen/entfernen, und alle diese Widgets wurden umgesetzt, wie Plugins).

Je nach Fall können Sie die Plugins sogar mit einem <Provider store={ theCoreStore }> umschließen, damit sie auf Redux zugreifen können oder einen Event-Bus einrichten, damit Plugins miteinander interagieren können ... Es gibt genug davon Sachen, um herauszufinden, voraus. :)

Viel Glück, hoffe es hat irgendwie geholfen!

+0

Können Sie bitte beschreiben 2. wo Sie Ihre Abhängigkeiten zu Fenster offen legen? Ich versuche * genau * das gleiche, aber ich weiß nicht wie. Ich verliere meine Haare gerade, wäre froh, wenn Sie einen Beispielcode zur Verfügung stellen. – AmazingTurtle

+0

Hey @BlackHat Ich mache einfach so etwas wie 'window.React = React; window.redux = redux' in meiner index.js (eigentlich in einer separaten Datei, die den App Reducer vorbereitet usw.). Importieren Sie einfach Ihre Abhängigkeiten in Ihre Hauptanwendung und exportieren Sie sie erneut in das Fenster. Da Plugins asynchron geladen werden, wenn sie schließlich ausgeführt werden, haben sie die window.XXXX-Variablen bereits bereit. – CharlieBrown

+0

Wie sagen wir den Plugins, Import aus dem Fenster zu verwenden. * Statt require()? – AmazingTurtle

Verwandte Themen