摸鱼无聊怎么办,怼着代码死里干!
为了接触更多的新技术,拓展自己的技术能力,本文给大家带来我第一次使用electron的技术文章,记录了对electron的一些基本的认识,主要介绍了electron的一些基本的架构和api的使用,由于对react的熟悉度还是比较低,所以这次选择的脚手架是electron-react
快速安装
先简单引入一段 electron-react-boilerplate 的安装模版
js
# Clone the boilerplate:
git clone --depth=1 \
https://github.com/electron-react-boilerplate/electron-react-boilerplate \
your-project-name
cd your-project-name
# Install dependencies:
npm install
先看最终运行效果
安装问题
将项目克隆到本地后会出现无法安装的报错
解决方案
- 清理
npm
缓存: 尝试清理npm
的缓存,运行以下命令来清理npm
缓存:
js
npm cache clean --force
- 删除
node_modules
和package-lock.json
: 有时候,node_modules
和package-lock.json
中的一些缓存或冲突可能导致问题。需要尝试删除它们并重新安装依赖:
js
rmdir /s /q node_modules
del package-lock.json
npm install
- 重新运行即可解决问题
electron结构
我们先简单了解一下 electron
主要分为主进程和渲染进程 为了下面的代码解析做一个基础
主进程
"main": "./src/main/main.ts"
是在 package.json
文件中指定 Electron 应用程序主进程(Main Process)入口文件的配置。
渲染进程
在主窗口载入渲染进程页面
js
//main
mainWindow.loadURL(resolveHtmlPath('index.html'));
js
export function resolveHtmlPath(htmlFileName: string) {
if (process.env.NODE_ENV === 'development') {
const port = process.env.PORT || 1212;
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
}
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
}
通过该方式将渲染进程加载到窗口中
主进程解读
1.导入依赖
首先导入了一些 Electron 相关的模块,包括path
、app
、BrowserWindow
、shell
和ipcMain
等。还导入了其他模块,如electron-updater
用于自动更新应用、electron-log
用于记录日志等。
js
import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
主进程和渲染进程通信
上面导入的ipcMain主要是用于主进程和渲染进程之间的通信
主进程监听了名为 'ipc-example' 的 IPC 事件。当渲染进程发送 'ipc-example' 事件时,主进程将打印接收到的消息,并回复 'ipc-example' 事件,回复内容为 'IPC test: pong'。
js
//main
ipcMain.on('ipc-example', async (event, arg) => {
const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
console.log(msgTemplate(arg),'收到渲染进程信息');
event.reply('ipc-example', msgTemplate('pong'));
});
js
/renderer
// calling IPC exposed from preload script
window.electron.ipcRenderer.once('ipc-example', (arg) => {
// eslint-disable-next-line no-console
console.log(arg,'收到主进程信息');
});
渲染进程的打印在启动窗口中打印
主进程在vscode控制台打印,我们可以看到已经接收到渲染进程发送的信息,但是结果是乱码
解决控制台乱码 :
启动的命令中加上 chcp 65001
js
"start": "chcp 65001 && ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
开发环境配置
代码中检查了环境变量 NODE_ENV
和 DEBUG_PROD
,以确定是否为开发环境。如果是开发环境,会调用 electron-debug
模块来支持 Electron 的开发调试。
js
const isDebug =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';
if (isDebug) {
require('electron-debug')();
}
安装开发者工具扩展: 在开发环境下,通过 electron-devtools-installer
模块来安装开发者工具扩展,其中包括 REACT_DEVELOPER_TOOLS
。
js
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS'];
return installer
.default(
extensions.map((name) => installer[name]),
forceDownload
)
.catch(console.log);
};
创建主窗口
createWindow
函数用于创建 Electron 主窗口。这里定义了主窗口的属性,包括尺寸、图标、preload 脚本等,并加载 index.html
页面。在主窗口准备好显示后,会显示主窗口,也可以选择最小化窗口。
isDebug
:isDebug
是一个布尔值,表示是否处于调试模式。如果是调试模式,则会执行installExtensions
函数,安装开发工具扩展。RESOURCES_PATH
和getAssetPath
:这两个变量用于确定资源文件的路径。RESOURCES_PATH
是一个字符串,表示资源文件夹的路径。getAssetPath
是一个函数,接受一个或多个路径片段,然后返回它们在资源文件夹中的完整路径。mainWindow
:mainWindow
是一个 ElectronBrowserWindow
对象,用于创建和管理应用程序的主窗口。在这个函数中,它会被创建并设置一些属性,如窗口大小、图标、预加载脚本等。mainWindow.loadURL
:该方法用于加载应用程序的主页面,它会将指定的 HTML 文件加载到mainWindow
窗口中。mainWindow.on('ready-to-show')
:该事件监听器会在mainWindow
窗口加载完毕并准备好显示时触发。在这里,它会检查mainWindow
是否为 null,并根据process.env.START_MINIMIZED
的值决定是最小化窗口还是显示窗口。mainWindow.on('closed')
:该事件监听器会在mainWindow
窗口关闭时触发,用于清理资源并将mainWindow
设置为 null。menuBuilder.buildMenu()
:这是一个用于创建应用程序菜单的函数,将在mainWindow
中创建自定义菜单。mainWindow.webContents.setWindowOpenHandler()
:这是一个用于处理应用程序中链接的函数。它会在用户点击链接时打开默认浏览器,而不是在 Electron 中打开新窗口。
js
const createWindow = async () => {
if (isDebug) {
await installExtensions();
}
const RESOURCES_PATH = app.isPackaged
? path.join(process.resourcesPath, 'assets')
: path.join(__dirname, '../../assets');
const getAssetPath = (...paths: string[]): string => {
return path.join(RESOURCES_PATH, ...paths);
};
mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728,
icon: getAssetPath('icon.png'),
webPreferences: {
preload: app.isPackaged
? path.join(__dirname, 'preload.js')
: path.join(__dirname, '../../.erb/dll/preload.js'),
},
});
mainWindow.loadURL(resolveHtmlPath('index.html'));
mainWindow.on('ready-to-show', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
mainWindow.show();
}
});
mainWindow.on('closed', () => {
mainWindow = null;
});
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
// Open urls in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
return { action: 'deny' };
});
// Remove this if your app does not use auto updates
// eslint-disable-next-line
new AppUpdater();
};
- 应用事件监听: 对应用的一些事件进行监听,如
window-all-closed
事件,当所有窗口关闭时,退出应用(在 macOS 中会保持应用在内存中)。还有app.whenReady()
事件,在应用准备好后创建主窗口,并在 macOS 中点击 dock 图标时重新创建窗口。 - 自动更新: 代码中调用了
electron-updater
的autoUpdater
来进行自动更新检查。自动更新功能需要结合应用更新服务器,当有新版本可用时,应用会自动下载并应用更新。
自动更新
自动更新: 代码中调用了 electron-updater
的 autoUpdater
来进行自动更新检查。自动更新功能需要结合应用更新服务器,当有新版本可用时,应用会自动下载并应用更新。
js
class AppUpdater {
constructor() {
// log.transports.file.level = 'info';
log.transports.console.format = '{h}:{i}:{s} {text}';
log.transports.console.level = 'debug'; // 设置日志级别为 debug 或其他级别
autoUpdater.logger = log;
autoUpdater.checkForUpdatesAndNotify();
}
}