|
每个程序员都会想拥有属于自己的个人博客,网络就像一片广阔无边的星空,如果其中有一颗属于自己的星球是多令人向往的事情。
当然我也不例外,根据个人喜好模仿一家专门做设计的网站,也创建一个我自己的星球。

部署完自己的博客肯定就会想马上分享给自己的朋友,又急冲冲在租了一个云服务器,搭建一个简单的服务器。
在朋友分享(炫耀)后,觉得博客有点空洞,没想好怎么改造我的星球。
因为我本身是做桌面端应用,自然就想到可不可以把我的博客变成一个独立的应用,很快在万能的知乎上找到一个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(&#34;min&#34;)} />
<button className={Style.headerMax} onClick={() => ipcRenderer.send(&#34;max&#34;)} />
<button className={Style.headerClose} onClick={() => ipcRenderer.send(&#34;close&#34;)} />对应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(&#39;/publisher/<user_name>&#39;)
def publisher_socket(user_socket, user_name):
publisher_socket_dict[user_name]=user_socket
print(&#39;publisher 新增socket:&#39;, 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(&#39;publisher 发送:&#39;, u_name, user_msg)
except WebSocketError as error:
subscription_socket_dict.pop(u_name)
print(&#39;subscription 删除socket:&#39;, u_name, error)
except WebSocketError as error:
publisher_socket_dict.pop(user_name)
print(&#39;publisher 删除socket:&#39;, user_name, error)
@sockets.route(&#39;/subscription/<user_name>&#39;)
def subscription_socket(user_socket, user_name):
subscription_socket_dict[user_name]=user_socket
print(&#39;subscription 新增socket:&#39;, user_name)
web_gui_socket_dict={}
@sockets.route(&#39;/message/<user_name>&#39;)
def message_socket(user_socket, user_name):
web_gui_socket_dict[user_name]=user_socket
print(&#39;message 新增socket:&#39;, 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 == &#39;web&#39;:
if u_name == &#39;gui&#39;:
u_socket.send(user_msg)
print(&#39;message web->gui 发送:&#39;, user_msg)
elif user_name == &#39;gui&#39;:
if u_name == &#39;web&#39;:
u_socket.send(user_msg)
print(&#39;message gui->web 发送:&#39;, user_msg)
except WebSocketError as error:
web_gui_socket_dict.pop(user_name)
print(&#39;message 删除socket:&#39;, user_name, error)
http_server = WSGIServer((&#39;0.0.0.0&#39;, 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() << &#34;onSocketMsg&#34; << data;
QJsonDocument doc = QJsonDocument::fromJson(data.toLatin1());
QJsonObject obj = doc.object();
if(obj.contains(&#34;event&#34;))
{
if(obj[&#34;event&#34;].toString() == &#34;onHandleEvent&#34;)
{
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() << &#34;error websocket:&#34; << 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左右的大小,头疼。 |
|