我们在Angular上创建了一个跨平台的本地桌面应用程序

角节点


您可能已经知道,Angular已经存在于许多平台上:



好吧,当然,这里没有足够的台式机(现在还不谈论Electron)。


为了创建桌面应用程序,有许多使用模板的解决方案,例如JavaFx,Qt,WPF之类的解决方案。 除了最后一个,所有这些都是跨平台的。


但是,如果我们想使用熟悉的框架并在其上创建本机应用程序,该怎么办? 实际上,这就是我所做的。


首先,我研究了当前可用的功能,以及在Angular下可能已经完成的操作。


实际上,我想以此方式证明您可以在Angular上做任何事情,而且我已经连续几年做了各种各样的事情

搜寻


libui节点


它是一个轻巧的可移植GUI库,可针对其支持的每个平台利用GUI的本机功能。 它是电子的替代品。


一个简单的应用示例:


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

在引擎盖下,他有简单的libui资料夹。 (libui:用于C的可移植GUI库)。 所有这些都是通过node-gyp进行的,该工具旨在为Node.js编译本机扩展。 libui-node包含30多个现成的组件,如果您突然决定创建一些自定义组件,则需要深入研究C语言中的代码。此外,这些组件本身是2年前编写的,从那时起更新。 也许一切都很好,所以不需要进行更改,这30个组件足以进行开发,或者根本没有人需要该项目。


好吧,实际上,完成的应用程序可能如下所示:


libui节点 libui节点


质子和维多


这里有点有趣,质子本机和vuido是同一个libui节点,仅在React和Vue下。 相应的包装器被编写在libui-node的组件下。 尽管github上有很多星星(9k和6k),但这些项目还是被放弃了,几乎没有人使用它们。 在我能找到的所有东西中,这些都是非常简单的应用程序。 我发现的另一个问题是UI本身的自定义问题,不可能在libui中完成,该项目的作者正在考虑重写Qt中的所有内容。


Libui本身非常适合编写各种绑定,发烧友甚至在php中也将其拖动

完成的应用程序可能如下所示:


质子化的 质子化的


没有自定义的界面非常无聊,因此该选项立即消失了。


还是接受Qt?


Qt,JS,CSS


当然,您听说过Qt,而且它随处可见,但很少有人听说过它现已与Javascript集成在一起。 QML允许使用属性绑定器以声明方式构造用户界面,从而扩展现有QML元素的功能。 当然,这比Web上的Javascript更严格。 您可以使用QML对象编写类似于ES5的内容,但是您将没有DOM API。


简要说明一下,如何在C ++下用Qt编写:


 #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 *} 

Qml中的代码可能如下所示:


 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)) } } 

这些组件可以动态创建。


QML也有一个大型的类型系统,当在Typescript中定义所有这些类型时 ,无疑将非常有用。


您还可以轻松自定义组件:


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

几乎是CSS,不是吗?


Qt在大多数平台上可以做的就是要添加的所有内容。


我们将拥有Node.js


当搜索“ nodejs + qt”时,我们将立即获得node-qt ,但它立即引起人们的注意:该产品早已失效,并且上一次出现生命迹象是在8年前。


不过,在搜索中您可以找到一个非常新鲜的项目-NodeGui。


NodeGui


像Gui的许多库一样,Qt使用其事件/消息循环来处理来自小部件的事件。 因此,当我们有条件地调用app.exec()时,Qt将启动消息循环并将其阻塞在那里。 当整个应用程序中只有一个消息循环时,所有这些都很好。 但是,由于我们需要将Qt与NodeJ一起使用,并且后者还具有自己的事件循环,因此不可能如此轻松地集成它们。 但是已经做出了这样的决定,例如,与Electron或yode捆绑在一起。 这些解决方案各有特点,它们至少引发两个进程-主线程和渲染器。 尽管如此,这种方法还是有很大的收获,不需要修改NodeJ或Chromium。


在使用NodeGui的情况下,情况略有不同,每个事物都有一个进程,因此,无需弄乱进程之间的事件。 为此,Nodejs被派生了,并且对Qt中必需的活页夹进行了较小的改进。 现在,您不需要像通常的node main.js一样启动进程,而是像qode main.js一样启动进程。 幸运的是,qode在@ nodegui / qode包中作为npm模块发布。 为了开始一个简单的问候世界,您需要安装一些软件包,有关每种操作系统的更多详细信息,请参见官方网站: https : //docs.nodegui.org/docs/guides/getting-started


默认情况下,在nodegui中,所有内容都是小部件,可以将它们固定在各种模板上。 当前,nodegui中有两种类型的模板:FlexLayout和QGridLayout。


Nodegui中的样式


目前,您可以内联和通过styleSheet设置小部件的样式。


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

默认情况下,Qt支持所有CSS2选择器( https://doc.qt.io/qt-5/stylesheet-syntax.html#selector-types


如果没有用于样式化组件的自定义属性,它也不是没有。 幸运的是,此类属性已在Qt的扩展坞中进行了描述,并在stackoverflow上进行了介绍。


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

角度的


该项目的作者已经实现了反应支持,但是当然,每个人都忘记了Angular的存在。

如开始时所写,Angular可以使用大多数平台,但是到目前为止,还没有用于桌面的平台。 由于精心设计和结构化的Angular API,Angular下的nodegui的实现归结为使用Renderer编写自定义platformBrowserDynamic并将其替换为应用程序。


但是,这一切如何由内而外起作用?


我们有条件的main.ts,我们将从它开始。


引导过程包括两个部分:创建平台和将启动模块放入其中。


 platformBrowserDynamic().bootstrapModule(AppModule); 

通过createPlatformFactory,我们可以创建您需要的任何平台。 对我们来说,这意味着我们不想使用普通的DOM,此外,在处理渲染时,我们将详细介绍元素的交互方案。 可在源代码中找到有关创建平台的更多信息。


在启动模块中,我们描述了首先渲染哪个组件。 创建组件实例时,Angular会调用renderComponent并将其与该组件实例接收的所需渲染关联。 Angular在组件渲染方面所做的一切(创建元素,设置属性,订阅事件等)都将通过此渲染器。 因此,我们需要替换RendererFactory。


首先,在Renderer中,我们将对createElement方法感兴趣。 在此方法中,我们获得标签的名称,然后需要从中创建所需的组件。 幸运的是,nodegui有一组基本的组件,我仔细移植并描述了它们将如何在Angular框架中呈现,并将所有内容都放入了常规组件目录中。 使用标准组件的其他操作也将通过此渲染器。 更多细节


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


要在渲染器中侦听事件,将抛出事件的名称,对于这些组件,我们将挂起通常的eventListener。


 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); } 

例如,组件事件与Qt完全相同,而不是通常的(click)=”clickFunc($event)”您必须编写(clicked) = ”clickFunc($event)”


当前有16个标准组件可用。 但是,如果您需要编写自定义组件,那么总是有机会通过QWidget进行此操作。


还制作了一个路由器,以使我们的应用程序与Angular尽可能兼容。


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

Angular Nodegui应用
Angular Nodegui应用


天气应用
天气应用


我们收集产品


为了构建现成的应用程序,nodegui拥有自己的@nodegui/packer.器- @nodegui/packer.


该实用程序非常简单,到目前为止由2个团队组成。


npx nodegui-packer-初始化myapp


此命令将创建一个包含模板的打包文件夹。 您可以更改内容以添加图标,更改应用程序的名称,描述和其他信息,以及添加必要的依赖项。


npx nodegui-packer-打包

此命令启动打包所需的工具(例如,macdeployqt for mac)并打包依赖项。


总结


总之,我想将结果与台式机上的其他Web解决方案进行比较(在Mac OS下启动的结果)。


下载大小 下载大小


内存使用 内存使用



链接到项目:
irustm /角节点
*使用Angular构建高性能,本机和跨平台的桌面应用程序。 Angular NodeGUI由Angular提供支持


有关项目的信息:
https://t.me/ngFanatic


有关我的开源项目的信息
https://twitter.com/irustm

Source: https://habr.com/ru/post/zh-CN480396/


All Articles