Electron学习笔记(一)

文章目录

      • 相关笔记
      • 笔记说明
    • 一、轻松入门
      • 1、搭建开发环境
      • 2、创建窗口界面
      • 3、调试主进程
    • 二、主进程和渲染进程
      • 1、进程互访
      • 2、渲染进程访问主进程类型
      • 3、渲染进程访问主进程自定义内容
      • 4、渲染进程向主进程发送消息
      • 5、主进程向渲染进程发送消息
      • 6、多个窗口的渲染进程接收主进程发送的消息
      • 7、渲染进程之间消息传递
        • 方法一:利用主进程进行中转
        • 方法二:单向传递

相关笔记

  • Electron学习笔记(一)
  • Electron学习笔记(二)
  • Electron学习笔记(三)
  • Electron学习笔记(四)
  • Electron学习笔记(五)
  • Electron学习笔记(六)
  • 使用 electron-vite-vue 构建 electron + vue3 项目并打包

笔记说明

文本为学习《Electron 实战 入门、进阶与性能优化 刘晓伦 著》时所记录的笔记 主要将书本上的案例运行一遍,针对原理部分并无相关记录。笔记记录于 2023年9月。

一、轻松入门

1、搭建开发环境

安装 yarn :

npm i -g yarn

创建一个文件夹,进行项目的初始化:

yarn init -y

配置 Electron 的镜像网站:

yarn config set electron_mirror https://registry.npmmirror.com/-/binary/electron/

使用 yarn 安装 Electron:

yarn add electron --dev

2、创建窗口界面

创建一个 index.html 文件,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Electron</title>
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>

新建一个 index.js 文件,内容如下:

const {app,BrowserWindow} = require('electron');

let win = null;

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true
        }
    });
    // 访问资源文件
    win.loadFile('index.html');

    // 程序启动后开启 开发者工具
    // win.webContents.openDevTools();
    
    win.on('close',function() {
        win = null;
    })
});

app.on('window-all-closed',function() {
    app.quit();
})

更新 package.json 文件:

"scripts": {
"start": "electron ./index.js"
},

启动项目:

yarn start

结果展示:

效果展示

3、调试主进程

点击调试按钮,创建 launch.json 文件 -> 选择Node.js环境

调试

修改 launch.json 文件如下:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "调试主进程",
            // type: 调试环境为 Node.js 环境
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceRoot}",
            // runtimeExecutable: 指向的是批处理文件,该批处理文件用于启动 Electron
            // ${workspaceRoot} 是正在进行调试的程序的工作目录的绝对路径
            "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
            "windows": {
                "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
            },
            // 此处的参数是主进程程序路径的简写形式,填写 "./index.js" 亦可
            "args": ["."],
            "outputCapture": "std"
        }
    ]
}

快捷键:

Ctrl+Shift+I:打开渲染进程的调试窗口

Ctrl+R:代码修改后,刷新界面

二、主进程和渲染进程

1、进程互访

注:原书籍中的代码由于 Electron 版本的更新,remote 模块无法直接导入使用,需要进行下载:

下载 remote 模块:

yarn add @electron/remote

更新 index.js 文件如下:(主进程代码

const {app,BrowserWindow} = require('electron');

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    require("@electron/remote/main").initialize();
    require("@electron/remote/main").enable(win.webContents);

    // 访问资源文件
    win.loadFile('index.html');

    // 程序启动后开启 开发者工具
    // win.webContents.openDevTools();

    win.on('close',function() {
        win = null;
    })
});

app.on('window-all-closed',function() {
    app.quit();
})

参考链接:https://blog.csdn.net/m0_45961428/article/details/122982510

在 index.html 添加以下代码:

<button id="openDevToolsBtn">打开开发者工具</button>
<script>
    const remote =require('@electron/remote');
    document.querySelector('#openDevToolsBtn').addEventListener('click',function() {
        remote.getCurrentWindow().webContents.openDevTools();
    })
</script>

运行程序:

yarn start

运行结果:(点击按钮可打开开发者工具)

运行结果

2、渲染进程访问主进程类型

主进程代码:主进程代码

更新 index.html 文件如下:

<button id="makeNewWindow">创建新窗口</button>
<script>
    const remote = require('@electron/remote');

    // 在渲染进程中创建一个新的窗口
    document.querySelector('#makeNewWindow').addEventListener('click',function() {
        win = new remote.BrowserWindow({
            webPreferences: {
                nodeIntegration: true,
            }
        });
        win.loadFile('newWin.html');
    });
</script>

说明:创建 BrowserWindow 的过程依然在主进程中进行,是由 remote 模块通知主进程完成相应的操作的,主进程创建了 BrowserWindow 对象的实例后,把对象的实例以远程对象的形式返回给渲染进程。

3、渲染进程访问主进程自定义内容

主进程代码:主进程代码

新建文件 mainModel.js:

let {BrowserWindow} = require('electron');

exports.makeWin = function() {
    let win = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true,
        }
    });
    return win;
}

更新 index.html 文件如下:

<button id="makeNewWindow2">创建新窗口2</button>
<script>
    const remote = require('@electron/remote');
    const mainModel = remote.require('./mainModel');

    let win2 = null;
    document.querySelector('#makeNewWindow2').addEventListener('click',function() {
        win2 = mainModel.makeWin();
        win2.loadFile('newWin.html');
    });
</script>

4、渲染进程向主进程发送消息

更新 index.html 文件:

<button id="sendMsg">向主进程发送消息</button>
<script>
    const {ipcRenderer} = require('electron');

    document.querySelector('#makeNewWindow2').addEventListener('click',() => {
        // msg:消息管道的名称
        ipcRenderer.send('msg',{name: 'xiaom'},{name: 'xiaoh'});
    });
</script>

index.js 文件添加以下内容:(其余主进程代码见:主进程代码)

const {ipcMain} = require('electron');

ipcMain.on('msg',(event,param1,param2) => {
    console.log(param1);
    console.log(param2);
    console.log(event.sender);
})

运行结果:

运行结果

5、主进程向渲染进程发送消息

在主进程 index.js 文件中添加以下代码:

const {app,BrowserWindow} = require('electron');
const {ipcMain} = require('electron');

let win = null;

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    // 监听渲染进程发来的消息,随后再次发回给渲染进程
    ipcMain.on('msg',(event,param1,param2) => {
        win.webContents.send('msg_main',param1,param2);
    })

    // 访问资源文件
    win.loadFile('index.html');

    win.on('close',function() {
        win = null;
    })
});

更新渲染进程 index.html 文件如下:

<button id="sendMsg">向主进程发送消息</button>
<script>
    const {ipcRenderer} = require('electron');

    // 接收 主进程发送的消息
    ipcRenderer.on('msg_main',(event,param1,param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    })

    document.querySelector('#sendMsg').addEventListener('click',() => {
        ipcRenderer.send('msg',{name: 'xiaom'},{name: 'xiaoh'});
    });
</script>

运行程序后 -> 点击按钮(向主进程发送消息) -> Electron 程序控制台将会打印主进程发送来的消息。

运行结果:

运行结果

6、多个窗口的渲染进程接收主进程发送的消息

更新主进程 index.js 文件:

const {app,BrowserWindow} = require('electron');
const {ipcMain} = require('electron');

// 接收 渲染进程 发送来的消息 在VSCode控制台打印消息
ipcMain.on('msg',(event,param1,param2) => {
    console.log(param1);
    console.log(param2);
    console.log(event.sender);
});

let win = null;

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    // 为了使 remote 模块能够使用需要执行以下操作
    require("@electron/remote/main").initialize();
    require("@electron/remote/main").enable(win.webContents);

    // 监听 渲染进程 发来的消息,随后再次发回给渲染进程
    ipcMain.on('msg',(event,param1,param2) => {
        // 单个窗口时使用:
        // win.webContents.send('msg_main',param1,param2);

        // 多个窗口时使用
        // 方法一:
        // event.sender.send('msg_main',param1,param2);
        // 方法二:
        event.reply('msg_main',param1,param2);
    })

    // 访问资源文件
    win.loadFile('index.html');

    win.on('close',function() {
        win = null;
    })
});

app.on('window-all-closed',function() {
    app.quit();
});

更新 index.html 文件如下:

<button id="makeNewWindow">创建新窗口</button>
<button id="sendMsg">向主进程发送消息</button>
<script>

    const remote = require('@electron/remote');
    const { ipcRenderer } = require('electron');

    // 在渲染进程中创建一个新的窗口
    document.querySelector('#makeNewWindow').addEventListener('click', function () {
        win = new remote.BrowserWindow({
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        win.loadFile('newWin.html');
    });

    // 监听主进程发送来的消息
    ipcRenderer.on('msg_main', (event, param1, param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    })

    // 点击发送按钮 发送消息至主进程
    document.querySelector('#sendMsg').addEventListener('click', () => {
        ipcRenderer.send('msg', { name: 'xiaom' }, { name: 'xiaoh' });
    });
</script>

newWin.html 文件内容如下:

<h1>newWindow</h1>
<button id="sendMsg2">向主进程发送消息</button>
<script>
    const { ipcRenderer } = require('electron');

    // 监听主进程发送来的消息
    ipcRenderer.on('msg_main', (event, param1, param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    })

    // 点击发送按钮 发送消息至主进程
    document.querySelector('#sendMsg2').addEventListener('click', () => {
        ipcRenderer.send('msg', { name: 'xiaod' }, { name: 'xiaoc' });
    });
</script>

7、渲染进程之间消息传递

一个程序有多个窗口,并要在窗口之间传递消息,可以通过主进程中转,此处通过win1先将消息发送给主进程,主进程再将消息发送给win2。

方法一:利用主进程进行中转

窗口(win1) --> 主进程(中转) --> 窗口(win2)

窗口(win2) --> 主进程(中转) --> 窗口(win1)

主进程 index.js 文件内容如下:

const {app,BrowserWindow} = require('electron');
const {ipcMain} = require('electron');

let win = null;

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    require("@electron/remote/main").initialize();
    require("@electron/remote/main").enable(win.webContents);

    // 监听 窗口win1 (index.html) 发来的消息
    ipcMain.on('msg_1',(event,param1,param2) => {
        // 向 窗口win1 (index.html) 发送消息
        win.webContents.send('msg_main',param1,param2);
    });

    // 访问资源文件
    win.loadFile('index.html');

    // 程序启动后开启 开发者工具
    win.webContents.openDevTools();

    win.on('close',function() {
        win = null;
    })
});

app.on('window-all-closed',function() {
    app.quit();
})

窗口(win1) index.html 文件内容如下:

<h1>win1</h1>
<button id="makeNewWindow">创建新窗口win2</button>
<button id="sendMsg">向主进程发送消息</button>
<script>

    const remote = require('@electron/remote');
    const { ipcRenderer } = require('electron');

    // 在渲染进程中创建一个新的窗口(win2)
    document.querySelector('#makeNewWindow').addEventListener('click', function () {
        win2 = new remote.BrowserWindow({
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        win2.loadFile('win2.html');
        win2.webContents.openDevTools();

        // 接收 主进程 的消息后 向 win2 发送消息
        ipcRenderer.on('msg_main', (event, param1, param2) => {
            win2.webContents.send('msg_win2',param1,param2);
        });
    });

    // 接收 主进程 发送的消息
    ipcRenderer.on('msg_main', (event, param1, param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    })

    // 点击按钮向 主进程 发送消息
    document.querySelector('#sendMsg').addEventListener('click', () => {
        ipcRenderer.send('msg_1', { name: 'xiaom' }, { name: 'xiaoh' });
    });
</script>

窗口(win2) win2.html 文件内容如下:

<h1>win2</h1>
<button id="sendMsg2">向主进程发送消息</button>
<script>
    const { ipcRenderer } = require('electron');

    // 接收 窗口 win1 (index.html) 发送来的消息
    ipcRenderer.on('msg_win2', (event, param1, param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    })

    // 点击按钮向 主进程 发送消息
    document.querySelector('#sendMsg2').addEventListener('click', () => {
        ipcRenderer.send('msg_1', { name: 'xiaod' }, { name: 'xiaoc' });
    });
</script>

结果展示:

结果展示

方法二:单向传递

窗口(win1) --> 窗口(win2)

主进程 index.js 文件内容如下:(此方法无需主进程中转,所以主进程无需接收消息)

const {app,BrowserWindow} = require('electron');

let win = null;

app.on('ready', function() {
    win = new BrowserWindow({
        // 为页面集成Node.js环境
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
        }
    });

    require("@electron/remote/main").initialize();
    require("@electron/remote/main").enable(win.webContents);

    // 访问资源文件
    win.loadFile('index.html');

    // 程序启动后开启 开发者工具
    win.webContents.openDevTools();

    win.on('close',function() {
        win = null;
    })
});

app.on('window-all-closed',function() {
    app.quit();
});

窗口(win1) index.html 文件内容如下:

<h1>win1</h1>
<button id="makeNewWindow">创建新窗口win2</button>
<button id="sendMsg">向窗口win2发送消息</button>
<script>

    const remote = require('@electron/remote');
    const { ipcRenderer } = require('electron');

    // 在渲染进程中创建一个新的窗口(win2)
    document.querySelector('#makeNewWindow').addEventListener('click', function () {
        win2 = new remote.BrowserWindow({
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        win2.loadFile('win2.html');
        win2.webContents.openDevTools();

        // 获取 窗口(win2) 的 webContents.id 并通过 ipcRenderer.sendTo 方法发送消息至 win2
        document.querySelector('#sendMsg').addEventListener('click', () => {
            ipcRenderer.sendTo(win2.webContents.id,'msg_win2', { name: 'xiaom' }, { name: 'xiaoh' });
        });
    });

</script>

窗口(win2) win2.html 文件内容如下:

<h1>win2</h1>
<script>
    const { ipcRenderer } = require('electron');

    // 接收 窗口(win1) 发送来的消息
    ipcRenderer.on('msg_win2', (event, param1, param2) => {
        console.log(param1);
        console.log(param2);
        console.log(event.sender);
    });
</script>

结果展示:

结果展示

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python】python淘宝交易数据分析可视化(源码+数据集)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

模型推导:BG/NBD(预测用户生命周期(CLV)模型)

CLV&#xff08;Customer Lifetime Value&#xff09;指的是客户生命周期价值&#xff0c;用以衡量客户在一段时间内对企业有多大的价值。企业对每个用户的流失与否、在未来时间是否会再次购买&#xff0c;还会再购买多少次才会流失等问题感兴趣&#xff0c;本文中的BG/NBD模型…

PostgreSQL数据库创建只读用户的权限安全隐患

PostgreSQL数据库模拟备库创建只读用户存在的权限安全隐患 default_transaction_read_only权限授权版本变更说明 看腻了就来听听视频演示吧&#xff1a;https://www.bilibili.com/video/BV1ZJ4m1578H/ default_transaction_read_only 创建只读用户&#xff0c;参照备库只读模…

第三步->手撕spring源码之基于Cglib实现实例化策略

为什么深入研究spring源码&#xff1f; 其实每一个程序员每天的工作都是一贯的CRUD 实现业务和需求完成的操作。几年这样的操作让我感觉在这方面要提神能力 光靠CRUD是绝对不可能的事情 CRUD只是满足你作为一个搬砖人而已。编程能力提升&#xff1f;其实更多的编程能力的提升是…

用 Supabase CLI 进行本地开发环境搭建

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;Supabase CLI&#xff08;1.1&#xff09;安装 Scoop&#xff08;1.2&#xff09;用 Scoop 安装 Supabase CLI &#xff08;二&#xff09;本地项目环境&#xff08;2.1&#xff09;初始化项目&#xff08;2…

Promise.all和 race

Promise.all() all方法可以完成并行任务&#xff0c; 它接收一个数组&#xff0c;数组的每一项都是一个promise对象。返回值&#xff1a; 成功时&#xff1a;当数组中所有的promise的状态都达到resolved的时候&#xff0c;就返回包含所有 Promise 结果的数组&#xff0c;并且…

【C++】————类与对象(上)-基础知识

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 类的两种定义方式&#xff1a; 成员变量命名规则的建议&#xff1a; 4.类的访问限定符及封装 4.1 访问限定符 ​编辑 【面试题】问题&#xff1a;C中struct和class的区别是什么&#xff1f; 4.2 封装 【面试…

数据分析中大数据和云计算

大数据和云计算 前言一、大数据二、大数据定义三、数据存储单位四、大数据存储技术五、大数据应用技术六、大数据特征七、数据容量八、数据类型的多样性结构化数据半结构化数据非结构化数据 九、获取数据的速度十、可变性十一、真实性十二、复杂性十三、价值十四、云计算十五、…

小白有什么副业可以做?

对于小白来说&#xff0c;以下是一些适合做副业的选择 1. 网络销售 可以在电商平台上开设小店&#xff0c;销售自己感兴趣的产品&#xff0c;如手工制品、二手物品、个人设计的商品等。 2. 做任务 目前网上最流行的就是做任务&#xff0c;因为简单无门槛&#xff0c;我推荐百…

partially initialized module ‘replicate‘ has no attribute ‘run‘

partially initialized module replicate has no attribute run(most likely due to a circular import) 在包名上停留查看impot 包的地址。 报错原因&#xff1a; 文件重名了&#xff0c;导入了 当前文件 。 修改文件名 即可。

分布式版本控制工具 - Git

文章目录 1. 概念介绍2. 客户端2.1 介绍2.2 仓库操作2.3 文件操作2.4 分支原理与操作2.5 标签2.6 远程仓库2.7 README与IGNORE 3. IDEA集成4. 版本号4.1 介绍4.2 文件操作4.2 分支操作 5. 命令5.1 介绍5.2 仓库操作5.3 文件操作5.4 分支操作5.5 标签操作5.6 远程仓库 1. 概念介…

【WebGPU】WebGPU 中的反应扩散计算着色器

在本教程中&#xff0c;我们将使用 WebGPU 技术中的计算着色器实现图像效果。更多精彩内容尽在数字孪生平台。 程序结构 主要构建两个 WebGPU 管道&#xff1a; 运行反应扩散算法多次迭代的计算管道&#xff08;js/rd-compute.js 和 js/shader/rd-compute-shader.js&#xff…

Linux学习之路 -- 文件系统 -- 缓冲区

前面介绍了文件描述符的相关知识&#xff0c;下面我们将介绍缓冲区的相关知识。 本质上来说&#xff0c;缓冲区就是一块内存区域&#xff0c;因为内核上的缓冲区较复杂&#xff0c;所以本文主要介绍C语言的缓冲区。 目录 1.为什么要有缓冲区 2.应用层缓冲区的默认刷新策略 …

【C++】STL — map和set的使用详细介绍

前言 本章将继续学习STL中的两个很重要的容器map和set&#xff0c;其底层实现是封装了一个红黑树&#xff0c;我们通过本节来学习和深入了解一下这两大容器。。。 序列式容器&#xff1a; string 、Vector、List 、dequeue 关联式容器&#xff1a;MAP 、SET、nordered_map、uno…

成员函数构造函数析构函数

文章目录 类的6个默认成员函数构造函数概述定义特性 析构函数概述特性 类的6个默认成员函数 空类&#xff1a; 如果一个类里面什么都没有写&#xff0c;我们称之为空类 class Date {};空类真的什么都没有吗&#xff1f; 实际上并非如此&#xff0c;编译器会自动生成6个默认成…

【大数据】HDFS

文章目录 [toc]HDFS 1.0NameNode维护文件系统命名空间存储元数据解决NameNode单点问题 SecondaryNameNode机架感知数据完整性校验校验和数据块检测程序DataBlockScanner HDFS写流程HDFS读流程HDFS与MapReduce本地模式Block大小 HDFS 2.0NameNode HANameNode FederationHDFS Sna…

C++笔试强训day19

目录 1.小易的升级之路 2.礼物的最大价值 3.对称之美 1.小易的升级之路 链接 模拟就行&#xff0c;唯一可能是难点得就是gcd&#xff08;最大公约数&#xff09; #include <iostream> using namespace std; #define int long long const int N 1e5 10; int arr[N];…

【DIY小记】深圳万象天地餐馆探店点评

第一次在技术博客里面写生活日记&#xff0c;也算是破了个小天荒。个人以为&#xff0c;博客是个人生活思考的载体&#xff0c;而技术只占生活的一部分&#xff0c;那么博客里为什么一定要限制只能够写技术内容&#xff0c;不能写点其它生活上的东西呢&#xff1f;思来想去&…

科研诚信与学术规范 2024年春 期末考试答案

章节答案&#xff1a;https://www.bilibili.com/video/BV1JZ42177F8/ 是这个课&#xff0c;网上的大多数答案都是以前的&#xff0c;跟这门课没啥关系. 期末考试的答案长这样&#xff0c;题库有80个题&#xff0c;考试一般是50个题。 期末考试答案&#xff1a;&#xff08;不…

C++动态内存区域划分、new、delete关键字

目录 一、C/C中程序的内存区域划分 为什么会存在内存区域划分&#xff1f; 二、new关键字 1、内置类型的new/delete使用方法&#xff1a; 2、new和delete的本质 一、C/C中程序的内存区域划分 为什么会存在内存区域划分&#xff1f; 因为不同数据有不同的存储需求&#xff0…