Wir erstellen eine plattformübergreifende native Desktop-Anwendung auf Angular

eckig-nodegui


Wie Sie wahrscheinlich bereits wissen, gibt es Angular bereits auf vielen Plattformen:



Natürlich gab es hier nicht genügend Desktops (lassen Sie uns noch nicht über Electron sprechen).


Für die Erstellung von Desktop-Anwendungen gibt es viele Lösungen, die Vorlagen verwenden, z. B. JavaFx, Qt, WPF. Mit Ausnahme der letzten sind alle plattformübergreifend.


Was aber, wenn wir das vertraute Framework verwenden und eine native Anwendung darauf erstellen möchten? Genau das habe ich getan.


Zunächst habe ich mir angesehen, was derzeit verfügbar ist und was möglicherweise bereits unter Angular ausgeführt wurde.


In der Tat möchte ich auf diese Weise zeigen, dass man auf Angular alles machen kann, und seit einigen Jahren mache ich alle möglichen Dinge für ihn .

Suche


libui-knoten


Es handelt sich um eine kompakte, tragbare GUI-Bibliothek, die die nativen Funktionen der GUI für jede unterstützte Plattform nutzt. Es ist eine Alternative zu Electron.


Ein einfaches Anwendungsbeispiel:


const win = new libui.UiWindow('Test window', 800, 600, false); 

Unter der Haube hat er einfache libui Bindemittel. (libui: eine portable GUI-Bibliothek für C). All dies geschieht über node-gyp, ein Hilfsprogramm zum Kompilieren nativer Erweiterungen für Node.js. libui-node enthält mehr als 30 vorgefertigte Komponenten, und wenn Sie sich plötzlich dazu entschließen, etwas Benutzerdefiniertes zu erstellen, müssen Sie sich mit dem Code in C befassen. Außerdem wurden die Komponenten selbst vor zwei Jahren und seitdem geschrieben aktualisiert. Vielleicht ist alles so gut, dass es nicht nötig ist, Änderungen vorzunehmen, und diese 30 Komponenten sind genug für die Entwicklung, na ja, oder niemand braucht das Projekt überhaupt.


Nun, tatsächlich könnte die fertige Anwendung so aussehen:


libui-knoten libui-knoten


Proton-native und Vuido


Und hier ist es etwas interessanter, Proton-native und Vuido ist der gleiche Libui-Knoten, nur unter React und Vue. Die entsprechenden Wrapper werden unter den Komponenten von libui-node geschrieben. Trotz der Anzahl der Sterne auf Github (9k und 6k) werden die Projekte aufgegeben und fast niemand verwendet sie. Von allem, was ich finden konnte, waren dies sehr einfache Anwendungen. Ein weiteres Problem, das ich entdeckt habe, sind Probleme mit der Anpassung der Benutzeroberfläche selbst. Es ist unmöglich, dies in libui zu tun, und der Autor des Projekts erwägt, alles in Qt neu zu schreiben.


Libui selbst ist sehr beliebt für das Schreiben von Bindungen aller Art, Enthusiasten haben es sogar in PHP geschleppt

Die fertige Anwendung könnte folgendermaßen aussehen:


Proton-native Proton-native


Eine ziemlich langweilige Oberfläche ohne Anpassung, sodass diese Option sofort verschwand.


Oder vielleicht Qt nehmen?


Qt, js, css


Natürlich haben Sie von Qt gehört und die Tatsache, dass es überall zu finden ist, aber nicht viele haben gehört, dass es jetzt sofort mit Javascript integriert ist. QML ermöglicht die deklarative Erstellung von Benutzeroberflächen mithilfe von Eigenschaftsbindern und damit die Erweiterung der Möglichkeiten vorhandener QML-Elemente. Natürlich ist dies strengeres Javascript als im Web. Sie können mit QML-Objekten etwas Ähnliches wie ES5 schreiben, haben jedoch keine DOM-API.


Nur eine kurze Anmerkung, wie würden Sie in Qt unter C ++ schreiben:


 #include <QApplication> #include <QPushButton> int main(int argc, char *argv[]) { QApplication app(argc, argv); *// Important * QPushButton hello("Hello world!"); hello.resize(100, 30); hello.show(); return app.exec(); *// Important *} 

Wie Ihr Code in Qml aussehen könnte:


 Item { function factorial(a) { a = parseInt(a); if (a <= 0) return 1; else return a * factorial(a - 1); } MouseArea { anchors.fill: parent onClicked: console.log(factorial(10)) } } 

Diese Komponenten können dynamisch erstellt werden.


QML hat auch ein großes Typensystem , das zweifellos nützlich sein wird, wenn all dies in Typescript definiert wird.


Sie können auch einfach Komponenten anpassen:


 Reactangle { id: redRectId width: 50 color: red } 

Fast CSS, nicht wahr?


Alles, was hinzugefügt werden muss, ist das, was Qt auf den meisten Plattformen kann.


Und wir hätten Node.js


Bei der Suche nach „nodejs + qt“ erhalten wir sofort node-qt , aber es fällt sofort auf, dass das Produkt seit langem tot ist und das letzte Mal vor 8 Jahren Lebenszeichen gezeigt hat.


Trotzdem kann man in der Suche ein sehr frisches Projekt finden - NodeGui.


NodeGui


Wie viele Bibliotheken für Gui verwendet Qt seine Ereignis- / Meldungsschleife, um Ereignisse von Widgets zu verarbeiten. Wenn wir daher app.exec () bedingt aufrufen, startet Qt die Nachrichtenschleife und blockiert sie dort. All dies ist gut, wenn in der gesamten Anwendung nur eine Nachrichtenschleife vorhanden ist. Aber da wir Qt mit NodeJs verwenden müssen und letztere darüber hinaus eine eigene Ereignisschleife haben, ist es unmöglich, sie so einfach zu integrieren. Solche Entscheidungen wurden aber bereits getroffen, zum Beispiel das gleiche Bündel mit Electron oder Yode. Diese Lösungen haben ihre eigene Besonderheit. Sie lösen mindestens zwei Prozesse aus - für den Haupt-Thread und für den Renderer. Trotzdem hat dieser Ansatz einen erheblichen Vorteil, da weder NodeJs noch Chrom modifiziert werden müssen.


Im Fall von NodeGui ist die Situation etwas anders, es gibt einen Prozess für alles und daher besteht keine Notwendigkeit, Ereignisse zwischen Prozessen zu fummeln. Nodejs wurde dafür gegabelt - und kleinere Verbesserungen an den erforderlichen Bindemitteln in Qt vorgenommen. Und jetzt müssen Sie den Prozess nicht wie gewohnt starten: main.js, sondern qode main.js. Glücklicherweise ist qode als npm-Modul im Paket @ nodegui / qode veröffentlicht. Um eine einfache Hallo-Welt zu starten, müssen Sie einige weitere Pakete installieren. Weitere Informationen zu den einzelnen Betriebssystemen finden Sie auf der offiziellen Website: https://docs.nodegui.org/docs/guides/getting-started


Standardmäßig ist in nodegui alles ein Widget, und sie können mit verschiedenen Vorlagen verschraubt werden. Derzeit gibt es in nodegui zwei Arten von Vorlagen: FlexLayout und QGridLayout.


Stile in Nodegui


Im Moment können Sie Stile für Widgets sowohl inline als auch über styleSheet festlegen.


 widget.setInlineStyle(`color: green`) view.setStyleSheet(` `#helloLabel { color: red; padding: 10px; } #worldLabel { color: green; padding: 10px; } #rootView { background-color: black; } `); 

Qt unterstützt standardmäßig alle CSS2-Selektoren ( https://doc.qt.io/qt-5/stylesheet-syntax.html#selector-types )


Es kommt auch nicht ohne benutzerdefinierte Eigenschaften für das Gestalten von Komponenten aus. Glücklicherweise sind solche Eigenschaften bereits in den Docks von Qt beschrieben und werden beim Stackoverflow gekaut.


 *QPushButton* { qproperty-iconsize: 20px 20px; } 

Angular


Der Autor des Projekts hat bereits Unterstützung für die Reaktion implementiert, aber natürlich hat jeder die Existenz von Angular vergessen.

Wie bereits zu Beginn geschrieben, kann Angular die meisten Plattformen ausführen, aber bisher gab es keine Plattform für den Desktop. Aufgrund der gut gestalteten und strukturierten Angular-API besteht die Implementierung von nodegui unter Angular darin, eine benutzerdefinierte platformBrowserDynamic mit Renderer zu schreiben und diese in der Anwendung zu ersetzen.


Aber wie funktioniert das alles von innen nach außen?


Wir haben eine bedingte main.ts und beginnen damit.


Der Startvorgang besteht aus zwei Teilen: Erstellen einer Plattform und Einfügen eines Startmoduls.


 platformBrowserDynamic().bootstrapModule(AppModule); 

Mit createPlatformFactory können wir absolut jede Plattform erstellen, die Sie benötigen. Für uns bedeutet dies, dass wir nicht mit dem üblichen DOM arbeiten möchten. Außerdem werden wir bei der Arbeit mit dem Render die Beschreibung des Interaktionsschemas der Elemente durchgehen. Weitere Informationen zum Erstellen der Plattform finden Sie in der Quelle.


Im Startmodul beschreiben wir, welche Komponente zuerst gerendert werden soll. Beim Erstellen einer Instanz einer Komponente ruft Angular renderComponent und verknüpft es mit der Instanz der Komponente, die es empfängt, und renderComponent es dem gewünschten Rendering zu. Alles, was Angular beim Rendern von Komponenten tut (Erstellen von Elementen, Festlegen von Attributen, Abonnieren von Ereignissen usw.), durchläuft diesen Renderer. Daher müssen wir die RendererFactory ersetzen.


In Renderer interessiert uns zunächst die Methode createElement. Bei dieser Methode erhalten wir den Namen des Tags und müssen daraus die gewünschte Komponente erstellen. Glücklicherweise verfügt Nodegui über einen grundlegenden Satz von Komponenten, die ich sorgfältig portiert und beschrieben habe, wie sie im Rahmen von Angular gerendert werden, wobei alles in das allgemeine Komponentenverzeichnis geworfen wird. Andere Aktionen mit Standardkomponenten durchlaufen diesen Renderer ebenfalls. Weitere Details .


[https://blog.nrwl.io/†(https://blog.nrwl.io/) https://blog.nrwl.io/


Um Ereignisse im Renderer abzuhören, wird der Name des Ereignisses geworfen, und für diese Komponenten wird der übliche eventListener aufgehängt.


 listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void { const callbackFunc = (e: NativeEvent) => callback.call(target, e); target.addEventListener(eventName, callbackFunc); return () => target.removeEventListener(eventName, callbackFunc); } 

Komponentenereignisse (click)=”clickFunc($event)” genau mit Qt (click)=”clickFunc($event)” Beispielsweise müssen Sie anstelle des üblichen (click)=”clickFunc($event)” schreiben.


Derzeit sind 16 Standardkomponenten verfügbar. Wenn Sie jedoch eine benutzerdefinierte Komponente schreiben müssen, besteht immer die Möglichkeit, dies über QWidget zu tun.


Ein Router wurde ebenfalls entwickelt, um unsere Anwendung so kompatibel wie möglich mit Angular zu machen.


 const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'about', component: AboutComponent } ]; // AppModule imports ... NodeguiRouterModule.forRoot(appRoutes), 

Angular Nodegui App
Angular Nodegui App


Wetter App
Wetter App


Wir sammeln in prod


Um eine fertige Anwendung zu erstellen, verfügt nodegui über einen eigenen Packer - @nodegui/packer.


Das Hilfsprogramm ist sehr einfach, bisher besteht es aus 2 Teams.


npx nodegui-packer - init myapp


Dieser Befehl erstellt einen Verpackungsordner mit der Vorlage. Sie können den Inhalt ändern, um ein Symbol hinzuzufügen, den Namen, die Beschreibung und andere Informationen der Anwendung zu ändern sowie die erforderlichen Abhängigkeiten hinzuzufügen.


npx nodegui-packer - pack

Dieser Befehl startet das für das Packen erforderliche Tool (z. B. macdeployqt for mac) und packt die Abhängigkeiten.


Abschließend


Abschließend möchte ich die Ergebnisse mit anderen Weblösungen auf dem Desktop vergleichen (Ergebnisse des Starts unter Mac OS).


Downloadgröße Downloadgröße


Speichernutzung Speichernutzung



Link zum Projekt:
Irustm / Angular-Nodegui
* Erstellen Sie mit Angular performante, native und plattformübergreifende Desktop-Anwendungen. Angular NodeGUI wird von Angular unterstützt


Informationen zum Projekt:
https://t.me/ngFanatic


Informationen zu meinen Open Source Projekten
https://twitter.com/irustm

Source: https://habr.com/ru/post/de480396/


All Articles