办公问答网

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 103|回复: 1

Electron

[复制链接]

1

主题

8

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-12-4 19:32:17 | 显示全部楼层 |阅读模式
每个程序员都会想拥有属于自己的个人博客,网络就像一片广阔无边的星空,如果其中有一颗属于自己的星球是多令人向往的事情。
当然我也不例外,根据个人喜好模仿一家专门做设计的网站,也创建一个我自己的星球。


部署完自己的博客肯定就会想马上分享给自己的朋友,又急冲冲在租了一个云服务器,搭建一个简单的服务器。
在朋友分享(炫耀)后,觉得博客有点空洞,没想好怎么改造我的星球。
因为我本身是做桌面端应用,自然就想到可不可以把我的博客变成一个独立的应用,很快在万能的知乎上找到一个Electron框架符合我的需求。
直接打开React项目,打开electron官网,参考官网的快速安装
yarn add electron在package.json里面添加执行入口和启动命令
  "main": "main.js",
  "homepage": "./",

  "scripts": {
    "start:main": "electron .",
    "start:render": "react-scripts start",
  },添加main.js内容,原生electron外框太丑,使用了自定义的标题栏
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const url = require('url');

const createWindow = () => {
    const win = new BrowserWindow({
        width: 1920,
        height: 1080,
        frame: false,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    })

    win.loadURL('http://localhost:3000');
    // win.webContents.openDevTools()

    ipcMain.on('min', () => {
        win.minimize()
    })

    ipcMain.on('max', () => {
        if (win.isMaximized()) {
            win.restore()
        } else {
            win.maximize()
        }
    })

    ipcMain.on('close', () => {
        win.close()
    })
}

app.whenReady().then(() => {
    createWindow()
    app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') {
            app.quit()
        }
    })
})
在React的标题栏添加拖拽区和最大最小关闭按钮
const {ipcRenderer} = window.require('electron')

<button className={Style.headerMin} onClick={() => ipcRenderer.send("min")} />
<button className={Style.headerMax} onClick={() => ipcRenderer.send("max")} />
<button className={Style.headerClose} onClick={() => ipcRenderer.send("close")} />对应css里面拖拽控件添加这个样式属性
-webkit-app-region: drag;直接运行一下看看效果如何

electron - 个人博客
https://www.zhihu.com/video/1580938563797053441
<hr/>前端界面实现了,但是作为一款桌面级应用,后端也是必须同步实现的。
之前服务端是用flask搭建的,走的都是http协议,很快我就发现前后通信问题。
没办法高效双向通信,http的通信模式是一种请求式单向通信,没办法满足应用层的MVC、MVP等等常见架构。
后来通过做前端朋友推荐websocket全双工通信可以解决,通过建立长连接特性,向上封装一层通信接口请求、订阅、查询接口。
from flask import Flask
from flask_sockets import Sockets
from geventwebsocket.handler import WebSocketHandler
from geventwebsocket.server import WSGIServer
from geventwebsocket.websocket import WebSocketError

app = Flask(__name__)
sockets = Sockets(app)

publisher_socket_dict={}
subscription_socket_dict={}

@sockets.route('/publisher/<user_name>')
def publisher_socket(user_socket, user_name):
   publisher_socket_dict[user_name]=user_socket
   print('publisher 新增socket:', user_name)

   while True:
      try:
         user_msg = user_socket.receive()
         if user_msg != None:
            for u_name,u_socket in subscription_socket_dict.items():
               try:
                  u_socket.send(user_msg)
                  print('publisher 发送:', u_name, user_msg)
               except WebSocketError as error:
                  subscription_socket_dict.pop(u_name)
                  print('subscription 删除socket:', u_name, error)
      except WebSocketError as error:
         publisher_socket_dict.pop(user_name)
         print('publisher 删除socket:', user_name, error)

@sockets.route('/subscription/<user_name>')
def subscription_socket(user_socket, user_name):
   subscription_socket_dict[user_name]=user_socket
   print('subscription 新增socket:', user_name)

web_gui_socket_dict={}
@sockets.route('/message/<user_name>')
def message_socket(user_socket, user_name):
   web_gui_socket_dict[user_name]=user_socket
   print('message 新增socket:', user_name)

   while True:
      try:
         user_msg = user_socket.receive()
         if user_msg != None:
            for u_name,u_socket in web_gui_socket_dict.items():
                  if user_name == 'web':
                     if u_name == 'gui':
                        u_socket.send(user_msg)
                        print('message web->gui 发送:', user_msg)
                  elif user_name == 'gui':
                     if u_name == 'web':
                        u_socket.send(user_msg)
                        print('message gui->web 发送:', user_msg)
      except WebSocketError as error:
         web_gui_socket_dict.pop(user_name)
         print('message 删除socket:', user_name, error)

http_server = WSGIServer(('0.0.0.0', 9090), application=app, handler_class=WebSocketHandler)
http_server.serve_forever()简单测试一下,用websocket代替flask做通信服务,react是可以和服务器正常通信。
<hr/>一个大胆的想法就冒出脑海,如果我之前一直是用Qt写桌面端应用,是不是可以在曾经写过的Qt项目里面添加一个websocket微服务。
因为我很多项目都是采用视图和数据分离的框架,那是不是可以用react来作为视图端的平替。
这样通过electron框架使用web前端好看的界面风格,可以快速还原ui图的动画效果,还有可以借用庞大的前端社区来丰富生态库。
而且同时简单搭个websocket服务器,屏蔽elelctron接口,又可以快速生产出web上位机。
而作为后端我可以使用最熟练的C++作为开发,可以接入各种串口通信,跟硬件通信无缝衔接,也可以调用window原生接口。
void ElecWebSocket::initServer(const QString &ip, int port)
{
    m_webSocketServer = new QWebSocketServer(ip, QWebSocketServer::NonSecureMode);
    m_webSocketServer->listen(QHostAddress::AnyIPv4, port);
    connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &QPhotoWebSocket::onSocketConnection);
}

void ElecWebSocket::onSocketConnection()
{
    if(m_webSocketServer->hasPendingConnections())
    {
        auto socket = m_webSocketServer->nextPendingConnection();
        connect(socket, &QWebSocket::textMessageReceived, this, &ElecWebSocket::onSocketMsg);
        connect(socket, &QWebSocket::disconnected, this, &ElecWebSocket::onSocketDisconnected);
        m_webSocketClient.append(socket);
    }
}

void ElecWebSocket::onSocketMsg(const QString &data)
{
    qDebug() << "onSocketMsg" << data;
    QJsonDocument doc = QJsonDocument::fromJson(data.toLatin1());
    QJsonObject obj = doc.object();
    if(obj.contains("event"))
    {
        if(obj["event"].toString() == "onHandleEvent")
        {
            onHandleEvent(obj);
        }
    }
}

void ElecWebSocket::onSocketDisconnected()
{
    auto socket = qobject_cast<QWebSocket*>(this->sender());
    m_webSocketClient.removeOne(socket);
}

void ElecWebSocket::send(const QJsonObject &data)
{
    QJsonDocument doc;
    doc.setObject(data);
    for(QWebSocket* socket : m_webSocketClient)
    {
        if(socket->isValid())
        {
            socket->sendBinaryMessage(doc.toJson(QJsonDocument::Compact));
        }
        else {
            qDebug() << "error websocket:" << socket->errorString();
            m_webSocketClient.removeOne(socket);
        }
    }
}
<hr/>开心的使用了一段时间,我又发现一些弊端。
最大的一个问题就是,性能,性能,还是性能。
因为我是做3d方向,涉及到对三维模块的显示和编辑,之前用vtk用得那一套处理方式,平替到three.js上面。
研究了一段时间发现,three.js适合做显示,但对应模型编辑处理上面提供接口太少,然后想到一个曲折得办法,通过vtk先处理,然后把数据结果刷新到three.js上面。
  const updateModel = () => {
    if (stateGeometry !== undefined)
    {
      const positions = stateMesh.geometry.attributes.position.array
      for(let i = 0; i < positions.length & i < stateGeometry.length; i++)
      {
        positions = stateGeometry / 1000;
      }
      stateMesh.geometry.attributes.position.needsUpdate = true
    }
  }
然后就遇到头疼的问题,不知道是不是我刷新代码有问题,还是web渲染性能问题,出现卡顿情况。
websocket传输也受到挑战,对于大数据量吞吐也出现延迟。
现在还在办法优化中。。。
还有一个问题就是安装包太大了,electron背锅,看到打完程序包,一个G左右的大小,头疼。
回复

使用道具 举报

1

主题

6

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2025-5-17 22:25:51 | 显示全部楼层
确实不错,顶先
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|办公问答网

GMT+8, 2025-7-5 21:32 , Processed in 0.083195 second(s), 22 queries .

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc. Templated By 【未来科技 www.veikei.com】设计

快速回复 返回顶部 返回列表