Electron -- 技巧总结

electron技巧总结

这篇文章整理了一些在electron开发中经常使用到的一些技巧;这些技巧大多数是细枝末节的知识,将其收集起来方法自己和其他人使用。

1. 调试用日志

不同于web端,在electron上debug是比较麻烦的,所以使用日志就非常有必要了。下面是用winston第三库创建的一个日志系统及使用举例:

js 复制代码
const { createLogger, transports, format } = require('winston');  
const path = require('path');  
// 创建一个 logger 实例  
logger = createLogger({  
    level: 'info', // 设置日志输出级别  
    format: format.combine(  
        format.timestamp(), // 添加时间戳  
        format.simple(), // 使用简单的文本格式输出日志信息  
    ),  
    transports: [  
        // new transports.Console(), // 输出到控制台  
        new transports.File({  
            // 输出到文件  
            filename: 'logs/app.log',  
        }),  
    ],  
});  
  
logger.info(process.env.APP_URL);  

2. 捕获异常

2.1 捕获全局异常

js 复制代码
/**  
 * 监听未捕获的异常  
 */  
process.on('uncaughtException', (error) => {  
    logger?.error('error', error);  
    process.exit(1);  
});  

2.2 捕获溢出的promise的reject

js 复制代码
// 监听未捕获的 Promise 拒绝(Rejection)  
process.on('unhandledRejection', (reason, promise) => {  
    logger?.error('error', reason);  
    process.exit(1);  
});  

3. 获取当前的运行路径

js 复制代码
const appPath = path.resolve(process.execPath);  

4. 获取开发/运行环境下不同的资源基地址

js 复制代码
const APP_URL = process.env.NODE_ENV === 'development' ? process.env.APP_URL : 'http://localhost/';  

5. 获取当前所有的窗口

js 复制代码
const { BrowserWindow } = require('electron');  
const windows = () => BrowserWindow.getAllWindows();  

6. 注册一些常见的快捷键

6.1 注册快捷键时候使用的方法

js 复制代码
const registerShortcut = () => {  
    // 强制刷新  
    globalShortcut.register('ctrl+shift+r', () => BrowserWindow.getFocusedWindow()?.webContents?.reload());  
    // 在右侧打开devTool  
    globalShortcut.register('ctrl+shift+i', () => BrowserWindow.getFocusedWindow()?.webContents?.openDevTools({ mode: 'right' }));  
    globalShortcut.register('ctrl+shift+a', () => void 0);  
    globalShortcut.register('ctrl+shift+d', () => void 1);  
};  

6.2 注册方法的调用时机

对于窗口快捷键的注册时机应该在app.whenReady状态改变之后

js 复制代码
app.whenReady().then(() => {  
    ...  
    // 注册快捷键  
    registerShortcut();  
})  

6.3 释放快捷键的时机

取消快捷键注册的时机是app的quit钩子中

js 复制代码
app.on('quit', () => {  
    globalShortcut.unregisterAll();  
    removeRegKey();  
});  

7. 渲染进程主动获取主进程状态

使用ipcMain.handle和ipcRenderer.invoke搭配获取主进程上的数据/状态

主进程响应:

js 复制代码
function cb (e, params) {  
    return 'data';  
}  
ipcMain.handle('topic-x', cb);  

渲染进程请求:

js 复制代码
ipcRenderer.invoke('topic-x', params); // invoke的返回值是一个promise对象  

8. 主进程接受渲染进程发送的消息

使用ipcMain.on接受渲染进程发送的消息

js 复制代码
ipcMain.on('topic-y', (e, data) => {  
    // do something with data  
    e.reply('reply-topic-y', 'message get it');  
});  

9. 主进程主动向渲染进程广播消息

使用ipcMain.on接受渲染进程发送的消息

js 复制代码
BrowserWindow.getAllWindows()?.forEach( win => void win.webContents.send('broadcast-message', 'wolf is coming!'));  

10. 在主进程中发起get网络请求

使用axios发起网络请求

js 复制代码
const axios = require('axios');  
...  
const request = (method, url) => {  
    return axios({  
        method,  
        url,  
    })  
}  

11. electron以单例运行--单例锁

js 复制代码
const { app } = require('electron');  
const gotTheLock = app.requestSingleInstanceLock();  
if (!gotTheLock) {  
    app.quit();  
}  

12. 新建窗口

  • 传入初始化参数,实例化一个新的BrowserWindow对象
  • 设置新打开窗口的图标
  • 新窗口展示目标网址的内容
  • 新建窗口监视自定义的事件--用来和主进程进行通信
js 复制代码
const createWindow = (screen, prepareJS, url = APP_URL) => {  
    // 传入必要的参数,实例化出来一个新窗口  
    const win = new BW({  
        x: screen.bounds.x + 50 || 50,  
        y: screen.bounds.y + 50 || 50,  
        frame: false,  
        kiosk: true,  
        skipTaskbar: true,  
        fullscreen: true,  
        resizable: false,  
        minimizable: false,  
        transparent: true,  
        webPreferences: {  
            preload: path.join(__dirname, prepareJS),  
        },  
    });  
    // 将窗口设置为总是位于最顶层,并且在多个显示器时仅限于当前屏幕上显示  
    win.setAlwaysOnTop(true, 'screen-saver');  
    // 设置窗口的图标  
    win.setIcon(path.join(__dirname, '../icons/logo.png'));  
    // 在新建窗口上显示内容  
    win  
        .loadURL(url)  
        .then(() => {  
            win.webContents.send('new-window-established');  
        })  
        .catch(() => {});  
    // win监听主进程发送的消息,topic是established-fail  
    win.webContents.on('established-fail', (e) => void 0);  
    return win;  
};  

13. 根据pid关闭某个进程

根据进程的pid,使用child_process模块中的方法可以关闭此进程

js 复制代码
const exec = require('child_process').execSync;  
...  
const closeProcessById = (pid) => {  
    if (!pid) return;  
    try {  
        // 关闭语音助手进程  
        exec(`taskkill /F /PID ${pid}`);  
    } catch (e: unknown) {  
        logger?.error(`进程${pid}关闭失败!`);  
    }  
};  
  

14. 根据path打开某个服务

如果知道了某个服务所在的地址,就可以通过child_process模块中的方法打开这个服务

js 复制代码
const { spawn } = require('child_process');  
...  
let pid = '';  
const openProcessByPath = (path) => {  
    try {  
        const childProcess = spawn(path []);  
        pid = childProcess.pid;  
  
        // 语音助手的消息会打印在日志中  
        childProcess.stdout.on('data', (data) => {  
            vLogger.log('info', `子进程输出: ${data}`);  
        });  
  
        // 处理错误  
        childProcess.on('error', (error) => {  
            logger?.error('error', `子进程错误: ${error}`);  
            throw new Error('launch service failed');  
        });  
  
        // // 断线通知  
        // childProcess.on('close', (code) => {  
        //     logger?.log('info', `子进程退出,退出码: ${code}`);  
        // });  
    } catch (e: unknown) {  
        logger?.error('error', 'launch server failed');  
    }  
  
};  

15. 根据服务名称清除所有同名服务

同样是使用child_process中的exec方法

js 复制代码
const exec = require('child_process').execSync;  
...  
const clearServerByName = (processName) => {  
    const command = 'wmic process where "name=\'' + processName + '\'" get ProcessId /VALUE';  
    try {  
        const output = exec(command).toString('utf8');  
        const pidLine = output  
            .trim()  
            .split('=')  
            .filter((v: string) => v !== 'ProcessId');  
        if (pidLine?.forEach) {  
            pidLine.forEach((pid: string) => exec(`taskkill /F /PID ${parseInt(pid, 10)}`));  
        }  
    } catch (e: unknown) {  
        return;  
    }  
};  

16. 获取全部屏幕

js 复制代码
const { screen } = require('electron');  
const getScreens = () => {  
    return screen.getAllDisplays();  
}  

17. 获取主屏幕

js 复制代码
const { screen } = require('electron');  
const getPrimaryDisplay = () => {  
    return screen.getPrimaryDisplay();  
}  

18. 获取辅屏幕

js 复制代码
const { screen } = require('electron');  
const getSideScreens = () => {  
    return screen.getAllDisplays().filter((s) => s !== screen.getPrimaryDisplay());  
}  
相关推荐
神夜大侠1 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱1 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号1 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72932 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲2 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
究极无敌暴龙战神X2 小时前
前端学习之ES6+
开发语言·javascript·ecmascript
王解2 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里2 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱2 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster2 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python