前端 Node 项目迁徙为桌面 Electron 应用笔记

news2024/11/24 16:42:58

起因

我的服务器到期了,服务器上有几个服务,人家问这几个网站怎么不好使了,奈何服务器续费太贵租不起了…

但是服务还是要提供的,所以我在想如何把 node 的项目变成桌面端应用,于是有了这个笔记

效果展示

页面没啥变化,就是把 node 打包成 exe 应用了

在这里插入图片描述


技术选型

我是前端出身,这里偏向使用 js 去开发桌面端,想要短期解决客户的需求嘛,这里有几个技术选择:

  • Electron
  • Tauri
  • Wails

大致浏览了一下,这里选择使用 Electron,纯粹是官方说明第一眼看的比较顺,而且看论坛里关于它的讨论还挺多的,遇到问题估计能查到。


简单了解 Electron

类似浏览器套壳子,大概的组成如下图所示:

请添加图片描述

准备步骤:安装 Electron

首先把前端的整个项目复制一份,我们在复制的这份处理,然后打开 package.json,精简一下包内容(我是把 koa 都删掉了)

在这里插入图片描述

接着安装脚手架,(这里我是直接安装的 @electron-forge/cli, 方便以后打包,当然你也可以按照官方的直接安装 electron)官方的安装文档点我跳转!

yarn add --dev @electron-forge/cli
npx electron-forge import

安装 yarn add --dev @electron-forge/cli后如下图,devDependencies 字段添加了新的内容

在这里插入图片描述

npx electron-forge importpackage.json 里的样子如下:

在这里插入图片描述

运行完 npx electron-forge import 之后

  • 你的 package.json 会自动修改,添加了一些脚本和依赖
  • 自动生成了forge.config.js 文件
  • .gitignore 文件会帮我们添加一行 out/

之后就可以通过 yarn start 启动了!

开始:通过 Electron 打开页面!

我们打开 package.json,把 main 字段设置成 main.js(只是跟官网设置成一样,这里也可以不改,不过也要注意一下:electron 的主线程就是根据这个 main 字段声明的.js,所以我这里改一下~)

这里的迁徙过程其实有两个选项:

  • 新建:新建一个 main.js,当作 Electron 的主线程
  • 改造:在原有的 node 启动文件 index.js 里修改

先看一下怎么改的吧,工作量不大,大家根据自己的项目去思考怎么迁徙比较方便,这里为了逻辑清晰,我选择新建一个 main.js
(对于我的项目,之前 node 的接口之类的代码都不适用了…)

// main.js
const { app, BrowserWindow } = require('electron');
const path = require('node:path');

// explain: To handle the most common commands, such as managing desktop shortcuts, just add the following to the top of your main.js and you're good to go:
// [electron-squirrel-startup](https://github.com/mongodb-js/electron-squirrel-startup)
if (require('electron-squirrel-startup')) app.quit();

const createWindow = () => {
  // 声明应用的初始化窗口尺寸
  const mainWindow = new BrowserWindow({
    width: 1280,
    height: 960,
  });
  // 声明应用的主页面
  mainWindow.loadFile(path.join(__dirname, '/static/index.html'));
  // mainWindow.webContents.openDevTools(); // 打开开发者工具
};

// 应用准备完成之后创建窗口
app.whenReady().then(() => {
  createWindow();
});

这段代码有了之后就可以直接运行起来看看了~我们 yarn start 构建一下,构建成功后会自动弹出窗口

在这里插入图片描述

至此总算有点意思了对吧,我们接下来处理一下数据。

数据传递

Electron 里的数据传递不像前后端那样,前端带着数据请求后端接口,而是渲染线程向主线程数据传递,我们可以在官方中的流程模型介绍中深入理解这两个线程的具体作用,这里是进程间通信的实战。·

参考官方文档:

  • 流程模型
  • 进程间通信

我们先写个小 demo 来体验一下数据的传递

提示:之后的开发可能会经常用到控制台,所以我们在主进程中取消注释这句话 mainWindow.webContents.openDevTools(); // 打开开发者工具,方便我们调试

这里先新建 preload.js,注册事件,至于为什么新建这个文件,请查看进程间通信的官方文档(概括一下是为了安全)

然后我直接复制一下官方给的例子吧:

// preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer');
contextBridge.exposeInMainWorld('electronAPI', {
  setTitle: (title) => ipcRenderer.send('set-title', title),
});

接着,在 main.js 中将 preload.js 注册到页面

webPreferences: {
  preload: path.join(__dirname, 'preload.js'),
},

在这里插入图片描述

之后,构建一下,在页面中打印

在这里插入图片描述

可以看到,preload.js 里注册的 electronAPI 会挂载到前端的 window 变量上,我们通过注册的这些方法传递值

前端(渲染进程)===> 主进程

下面这张图是数据的传递过程

在这里插入图片描述

可以发发现,终端是乱码哎,我们可以这样做,在 package.json 的脚本的 start 字段 里添加 chcp 65001 &&

"start": "chcp 65001 && electron-forge start",

演示效果如下:

在这里插入图片描述
ok,终端也好了~

主线程和 preload.js 的代码如下,前端看场景可以自己写一下。

// main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');

// explain: To handle the most common commands, such as managing desktop shortcuts
// just add the following to the top of your main.js and you're good to go:
// [electron-squirrel-startup](https://github.com/mongodb-js/electron-squirrel-startup)
if (require('electron-squirrel-startup')) app.quit();

const createWindow = () => {
  // 声明应用的初始化窗口尺寸
  const mainWindow = new BrowserWindow({
    width: 1280,
    height: 960,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });

  // 声明应用的主页面
  mainWindow.loadFile(path.join(__dirname, '/static/index.html'));
  mainWindow.webContents.openDevTools(); // 打开开发者工具
};

function handleUploadFile(event, arg) {
  console.log('主线程 ipcMain.on 监听的 event:>>', event);
  console.log('主线程 ipcMain.on 监听的 arg:>>', arg);
}

// 应用准备完成之后创建窗口
app.whenReady().then(() => {
  createWindow();
  ipcMain.on('file:upload', handleUploadFile);
});
// preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer');
contextBridge.exposeInMainWorld('electronAPI', {
  uploadFile: (e) => ipcRenderer.send('file:upload', e),
});
// 前端 render.js
window.electronAPI.uploadFile({
  message: '渲染进程 window.electronAPI 传递的值',
  data: '我是发送的数据',
});

我们接着研究主线程如何传递数据给前端。

主进程 ===> 前端(渲染进程)

想说的都在图片里了!

在这里插入图片描述

部分代码如下:

// main.js
mainWindow.webContents.send('word-data', {
  code: 1,
  message: '我是主线程发送给前端的数据',
});
// preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer');

contextBridge.exposeInMainWorld('electronAPI', {
  uploadFile: (e) => ipcRenderer.send('file:upload', e),
  onReceiveData: (callback) => ipcRenderer.on('word-data', (_event, value) => callback(value)),
});
// 前端
beforeMount() {
  window.electronAPI.onReceiveData((value) => {
    console.log('前端 onReceiveData 的 value 值:>>', value);
  });
},

数据传递的总结

先整理一下这个 preload.js 的作用

在这里插入图片描述

然后就是一组通信的整理

功能代码
前端:发送window.electronAPI.AAA(data)
后端:接收window.electronAPI.BBB((value) => {})
后端:发送mainWindow.webContents.send('CCC', data)
前端:接收ipcMain.on('DDD', fn)

具体函数名看下图:

请添加图片描述

以上,算是比较清晰的整理一便,几乎可以做一些小玩具了,还有就是打包的问题,当然坑也是千奇百怪,遇到就自行搜索引擎吧,下面列一下我遇到的问题。

Q & A

项目中遇到的坑:开发环境和生产环境的 __dirname 值不一样

我的项目里有个输出文件的问题,输出文件的路径用到了__dirname

开发的时候都还好,打包之后功能就不对了,后来打印发现 __dirname 不同,因为打包后的源代码在 app.asar

请添加图片描述

于是我参考了这篇文章:关于electron的开发应用路径和生产路径的问题

asar 是什么

参考这篇博文:详解 Electron 中的 asar 文件

终端中文不显示,或者显示乱码

运行的时候添加 chcp 65001,可以在 package.json 中写好脚本

在这里插入图片描述
具体参考这篇博文: 解决vscode终端乱码问题【疑难杂症,使用chcp命令修改活动代码页无效的解决方法】

安装:安装 Electron 坎坷

核心的问题还是国内防火墙给墙住了,所以可以切换你包管理器的地址,更换成淘宝源的,我当时也按了一上午,很烦闷

这里推荐使用 yarn 安装,或者用 pnpm 也可以

官网的安装指南

打包:遇到 Making for target: squirrel - On platform: win32 - For arch: x64错误

这是打包的倒数第二个环节,这里出现问题我觉得影响不大,因为当前路径下应该有 out 的打包文件夹了,不过看着闹心的话,要具体看看控制器报的什么错误,那谷歌翻译一下那个依赖包出的错误。

打包:打包失败问题推断

这里推荐官网的 demo 打包测试一下,跟你自己的项目做个对照,而且 electron 的打包有好几个选择的,官网上推荐用 Electron Forge,其实还有 Electron Packager 等等,可以参考一下这篇文章了解一下:详解 Electron 打包

这是 Electron Forge 官网,其中有小型的 demo,你把它克隆下来然后启动并打包一下,如果可以打包成功的话,参考一下自己的项目里缺了什么。

可能缺少的选项有

  • package.json 的打包配置,具体看官网这篇文档 https://www.electronforge.io/import-existing-project#configuring-package.json(如果你是用cli 构建的项目,它会另外写一个forge.config.js 文件用于配置打包项,然而我的是已存在的项目,所以直接写在 package.json 里比较方便)
    在这里插入图片描述
  • cmd 管理员权限:最好用 管理员终端去打包,有些权限比较高,普通的终端可能没有权限
  • 一些依赖无法正确安装:推荐更换包管理器的下载源,具体可以看这篇文章:更换 npm 依赖下载源

打包报错:An unhandled rejection has occurred inside Forge:

Error: EPERM: operation not permitted, symlink 
'E:\Project_Front\electron-example\node_modules\.pnpm\electron-squirrel-startup@1.0.0\node_modules\electron-squirrel-startup'
-> 
'C:\Users\Wang\AppData\Local\Temp\electron-packager\tmp-UVFKGa\resources\app\node_modules\electron-squirrel-startup'

这里可能出现的愿意就是依赖没找到,首先检查一下 package.json 中的依赖都对不对,然后把 node_module 文件夹删掉,用 yarn 重新安装一下。

打包图标报错

参考这个博客:electron-forge打包如何自定义应用图标和安装动画

图标尺寸一定要大一点,不然生成的安装包会默认显示 electron 的图标,或者尝试清理 windows 的图标缓存,具体操作看下面

windows清理图标

将下面的文字放到笔记本里,然后保存,更改文件类型为 bat 并双击运行。

rem 关闭 Windows 外壳程序 Explorer
taskkill /f /im explorer.exe
rem 清理系统图标缓存数据库
attrib -h -s -r "%userprofile%\AppData\Local\IconCache.db"
del /f "%userprofile%\AppData\Local\IconCache.db"
attrib /s /d -h -s -r "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\*"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_32.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_96.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_102.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_256.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_1024.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_idx.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_sr.db"
rem 清理系统托盘记忆的图标
echo y reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v IconStreams
echo y reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v PastIconsStream
rem 重启 Windows 外壳程序 Explorer
start explorer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1372830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习10——免模型控制Q-learning算法

Q-learning算法 主要思路 由于 V π ( s ) ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V_\pi(s)\sum_{a\in A}\pi(a\mid s)Q_\pi(s,a) Vπ​(s)∑a∈A​π(a∣s)Qπ​(s,a) ,当我们直接预测动作价值函数,在决策中选择Q值最大即动作价值最大的动作&…

如何寻找到相对完整的真正的游戏的源码 用来学习?

在游戏开发的学习之路上,理论与实践是并重的两个方面。对于许多热衷于游戏开发的学习者来说,能够接触到真实的、完整的游戏源码无疑是一个极好的学习机会。但问题来了:我们该如何寻找到这些珍贵的资源呢? 开源游戏项目 GitHub:地…

BUUCTF ---> Encrypto

转眼就一月十号了,本来今天不想更的,(因为我懒)是因为明天要考python,好像还不止 但是呢,发现BUUCTF的密码学模块刚好可以用到py的脚本,那就当时复习一下吧!! 这里就要介…

http跟https有什么区别?

HTTPS和HTTP的概念: HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效&am…

2024--Django平台开发-Django知识点(六)

day06 Django知识点 今日概要: Form和ModelForm组件【使用】【源码】缓存【使用】ORM【使用】其他:ContentTypes、Admin、权限、分页、信号等 1.Form和ModelForm组件 背景:某个公司后台管理项目。 垃圾 def register(request):"&quo…

Qt/QML编程学习之心得:hicar手机投屏到车机中控的实现(32)

hicar,是华为推出的一款手机APP,有百度地图、华为音乐,更多应用中还有很多对应手机上装在的其他APP,都可以在这个里面打开使用,对开车的司机非常友好。但它不仅仅是用在手机上,它还可以投屏到车机中控上,这是比较神奇的一点。 HiCar本质上是一套智能投屏系统,理论上所有…

人工智能复习

机器学习中线性回归和逻辑回归: 机器学习的分类: 监督学习和无监督学习,半监督学习 监督学习(Supervised Learning): 监督学习是一种利用带有标签(标记)的数据进行训练的机器学习…

用友U8流程审批效率-SQLServer+SSRS

文章目录 @[TOC]1、 需求及效果1.1 需求1.2 效果2、 思路及SQL语句3、实现折叠明细表4、结语1、 需求及效果 1.1 需求 想要查看U8的审批流程,查看流程在哪个节点或人停留的时间,这个单据整个流程走下来需要的时间。可以更加直观方便的查看审批效率 1.2 效果 采用了SSRS上…

【算法每日一练]-动态规划 (保姆级教程 篇15) #纸带 #围栏木桩 #四柱河内塔

目录 今日知识点: 计算最长子序列的方案个数,类似最短路径个数问题 四柱河内塔问题:dp[i]min{ (p[i-k]f[k])dp[i-k] } 纸带 围栏木桩 四柱河内塔 纸带 思路: 我们先设置dp[i]表示从i到n的方案数。 那么减法操作中&#xff…

TensorRt(5)动态尺寸输入的分割模型测试

文章目录 1、固定输入尺寸逻辑2、动态输入尺寸2.1、模型导出2.2、推理测试2.3、显存分配问题2.4、完整代码 这里主要说明使用TensorRT进行加载编译优化后的模型engine进行推理测试,与前面进行目标识别、目标分类的模型的网络输入是固定大小不同,导致输入…

Docker中镜像的相关操作

1.辅助操作 docker version:用查看docker客户端引擎和server端引擎版本信息。 docker info:用来查看docker引擎的详细信息。 docker --help:用来查看帮助信息。 2.镜像Image docker images:查看当前本地仓库中存在哪些镜像。 …

Mysql是怎样运行的--下

文章目录 Mysql是怎样运行的--下查询优化explainoptimizer_trace InnoDB的Buffer Pool(缓冲池)Buffer Pool的存储结构空闲页存储--free链表脏页(修改后的数据)存储--flush链表 使用Buffer PoolLRU链表的管理 事务ACID事务的状态事…

在CentOS环境下编译GreatSQL RPM包

本文介绍如何在CentOS环境下编译GreatSQL RPM包。 运行环境是docker中的CentOS 8 x86_64: $ docker -v Docker version 20.10.10, build b485636$ docker run -itd --hostname c8 --name c8 centos bash a0a2128591335ef41e6faf46b7e79953c097500e9f033733c3ab37f…

使用curl命令在Linux中进行HTTP请求

在Linux中,curl是一个非常强大的命令行工具,用于发送HTTP请求。它允许用户发送各种类型的HTTP请求,如GET、POST、PUT、DELETE等,并能够处理响应数据。 首先,确保您的Linux系统已经安装了curl。如果未安装,…

Android 13 移除下拉栏中的设置入口

介绍 因为当前项目的设置已被加密,客户不希望通过下拉窗口的设置图标进入设置,决定去掉该图标。 效果展示 分析 这里首先想到在SystemUI寻找这个图标的资源文件,找到资源文件后寻找对应控件调用的地方,根据id寻找控件代码即可。…

2024年Google Ads新手指南——广告运作与类型、工具

谷歌广告投放是出海企业的必备运营动作,但你需要先了解他的运作逻辑、广告类型、投放必备的工具类型,之后可以为你的投放的高速转化做好万全准备,毕竟每一分钱都要花在刀刃上!废话不多说,下面开始为新手准备了基础指南…

【LLM】vLLM部署与int8量化

Acceleration & Quantization vLLM vLLM是一个开源的大型语言模型(LLM)推理和服务库,它通过一个名为PagedAttention的新型注意力算法来解决传统LLM在生产环境中部署时所遇到的高内存消耗和计算成本的挑战。PagedAttention算法能有效管理…

重置 Docker 中 Gitlab 的账号密码

1、首先进入Docker容器 docker exec -it gitlab bash 2、连接到 gitlab 的数据库 需要谨慎操作 gitlab-rails console -e production 等待加载完后会进入控制台 ------------------------------------------------------------------------------------------------------…

Page 251~254 Win32 GUI项目

win32_gui 源代码&#xff1a; #if defined(UNICODE) && !defined(_UNICODE)#define _UNICODE #elif defined(_UNICODE) && !defined(UNICODE)#define UNICODE #endif#include <tchar.h> #include <windows.h>/* Declare Windows procedure */…

知名开发者社区Stack Overflow发布《2023 年开发者调查报告》

Stack Overflow成立于2008年&#xff0c;最知名的是它的公共问答平台&#xff0c;每月有超过 1 亿人访问该平台来提问、学习和分享技术知识。是世界上最受欢迎的开发者社区之一。每年都会发布一份关于开发者的调查报告&#xff0c;来了解不断变化的开发人员现状、正在兴起或衰落…