1、安装
npm install -g electron / cnpm install -g electron
2、项目搭建方式
2-1、 克隆一个仓库、快速启动一个项目
克隆示例项目的仓库
git clone https://github.com/electron/electron-quick-start
进入这个仓库
cd electron-quick-start
安装依赖并运行
npm install && npm start
2-2、electron-forge 搭建一个 electron 项目
electron-forge 相当于 electron 的一个脚手架,可以让我们更方便的创建、运行、打包
electron 项目。
Github 地址:https://github.com/electron-userland/electron-forge
官网地址:https://www.electronforge.io/
如果你电脑上面安装了最新版本的 nodejs 可以使用 npx 创建项目,如果你电脑上面
安装了 yarn 也可以使用 yarn 创建项目
创建项目
npx create-electron-app my-new-app
或者
yarn create electron-app my-new-app
运行项目
cd my-new-app
npm start
2-3、手动搭建一个 electron 项目
1、新建一个项目目录 例如:electrondemo
2、在 electrondemo 目录下面新建三个文件: index.html、main.js 、package.json
3、index.html 里面用 css 进行布局(以前怎么写现在还是怎么写)
4、在 main.js 中写如下代码:
const { app, BrowserWindow } = require("electron")
const path = require("path")
const createWindow=()=>{
const mainWindow = new BrowserWindow({width: 800, height: 600, });
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// mainWindow.loadURL('https://github.com');
}
//监听 electron ready 事件创建窗口
app.on('ready', createWindow);
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
5、运行
electron .
2.1、关于主进程和渲染器进程
package.json 中定义的入口被称为主进程。 在主进程中实例化 BrowserWindow 创建的
Web 页面被称为渲染进程。一个 Electron 应用只有一个主进程,但是可以有多个渲染进程,
每个 Electron 中的 web 页面运行在它自己的渲染进程中。
主进程使用 BrowserWindow 实例创建页面。 每个 BrowserWindow 实例都在自己的
渲染进程里运行页面。 当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终
止。
2.2、主进程和渲染进程实战演示
const { app, BrowserWindow } = require("electron");
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 600, height: 400
});
mainWindow.loadFile(path.join(__dirname, "index.html"));
const secondWindow=new BrowserWindow({
width: 300, height: 200, parent:mainWindow, })
secondWindow.loadFile(path.join(__dirname, "second.html"));
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
3.1、主进程中使用 Nodejs 模块
Electron 主进程中无需任何配置就可以使用 nodejs 模块。
3.2、渲染进程中使用 Nodejs 模块
1、BrowserWindow 中通过 preload 加载的 js 文件可以直接使用 nodejs 模块
2、如果不使用 preload 加载的 js,Electron5.x 之后没法在渲染进程中直接使用 nodejs,如
果我们想在渲染进程中使用 nodejs 的话需要进行如下配置。
https://www.electronjs.org/docs/latest/api/browser-window
mainWindow=new BrowserWindow({width:800,height:600, webPreferences: {
nodeIntegration: true,//nodeIntegration Boolean (可选) - 是否启用Node integration. 默认值为 false.
contextIsolation:false
}});
3.3、BrowserWindow 中通过 preload 加载的 js 文件可以直接使用 nodejs 模块
在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成 Node, 此脚本都可以访
问所有 Node API 脚本路径为文件的绝对路径
main.js 主进程代码:
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800, height: 600, webPreferences:{
preload:path.join(__dirname, "renderer/preload.js")
}
});
mainWindow.loadFile(path.join(__dirname, "index.html"));
}
renderer/preload.js 渲染进程代码:
const fs =require("fs");
fs.readFile("package.json",(err,data)=>{
if(err){
console.log(err)
return;
}
console.log(data.toString());
})
3.4、渲染进程中直接使用 Nodejs 模块
如果不使用 preload 加载的 js,Electron5.x 之后没法在渲染进程中直接使用 nodejs,如果
我们想在渲染进程中使用 nodejs 的话需要进行如下配置。
mainWindow=new BrowserWindow({width:800,height:600, webPreferences: {
nodeIntegration: true, contextIsolation:false
}});
主进程:
const { app, BrowserWindow } = require("electron");
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800, height: 600, webPreferences:{
// preload:path.join(__dirname, "renderer/preload.js"), nodeIntegration:true, contextIsolation:false
}
});
mainWindow.loadFile(path.join(__dirname, "index.html"));
//打开 DevTools
mainWindow.webContents.openDevTools();
}
渲染进程:
const fs =require("fs");
fs.readFile("package.json",(err,data)=>{
if(err){
console.log(err)
return;
}
console.log(data.toString());
})
3.5、渲染进程和主进程中使用 Nodejs 第三方模块
1、安装模块
2、引入模块
3、参考第三方文档使用
mainWindow.webContents.openDevTools();
5-1、H5 的拖放 api
https://www.w3cschool.cn/jsref/event-ondragover.html
1、拖拽事件的定义和用法:
ondragover 事件在可拖动元素或选取的文本正在拖动到放置目标时触发。
默认情况下,数据/元素不能放置到其他元素中。 如果要实现改功能,我们需要防止元
素的默认处理方法。我们可以通过调用 event.preventDefault() 方法来实现 ondragover 事
件。
注意: 为了让元素可拖动,需要使用 HTML5draggable 属性。
提示: 链接和图片默认是可拖动的,不需要 draggable 属性。
2、在拖放的过程中会触发以下事件:
在拖动目标上触发事件 (源元素):
ondragstart - 用户开始拖动元素时触发
ondrag - 元素正在拖动时触发
ondragend - 用户完成元素拖动后触发
释放目标时触发的事件:
ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
ondrop - 在一个拖动过程中,释放鼠标键时触发此事件
注意: 在拖动元素时,每隔 350 毫秒会触发 ondragover 事件。
5-2、Electron 拖放打开文件
index.html
Document
主进程main.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration:true, //开启渲染进程中使用nodejs
contextIsolation:false //开启渲染进程中使用nodejs In Electron 12, the default will bechanged to `true
}
});
//在渲染进程中开启调试模式
mainWindow.webContents.openDevTools()
mainWindow.loadFile(path.join(__dirname, "index.html"));
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
渲染进程renderer文件夹下index.js
/*
https://www.w3cschool.cn/jsref/event-ondragover.html
ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
ondrop - 在一个拖动过程中,释放鼠标键时触发此事件
*/
const fs=require("fs");
window.onload=()=>{
var contentDom=document.querySelector("#content");
//阻止默认行为
contentDom.ondragenter=contentDom.ondragover=contentDom.ondragleave=()=>{
return false;
}
contentDom.ondrop=(e)=>{
// console.log(e)
console.log(e.dataTransfer.files[0].path);
var path=e.dataTransfer.files[0].path;
fs.readFile(path,(err,data)=>{
if(err){
console.log(err);
return false;
}
contentDom.innerHTML=data
})
}
}
6-1、Electron 主进程和渲染进程中的模块(介绍)
官方文档:https://www.electronjs.org/zh/docs/latest/api/app
6-2、Electron remote 模块
remote 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。
Electron 中, 与 GUI 相关的模块(如 dialog, menu 等)只存在于主进程,而不在渲染进程中 。为了能从渲染进程中使用它们,需要用 ipc模块来给主进程发送进程间消息。使用 remote 模
块,可以调用主进程对象的方法,而无需显式地发送进程间消息,这类似于 Java 的 RMI。
注意:Electron10.x 之前可以直接使用 Remote 模块,Electron10.x 以后 Electron14.x 以前要使用 remote 模块的话必须得在 BrowserWindow 中通过 enableRemoteModule: true 开启Electron14.x 以后官方把内置的 remote 挪到了第三方模块里面,下面我们给大家看看如何在Electron 最新版本中使用@electron/remote 模块
6-3、Electron 渲染进程中通过 remote 模块调用主进程中的 BrowserWindow 打开新窗口
1、安装@electron/remote
npm install --save @electron/remote
或者
cnpm install --save @electron/remote
或者
yarn add @electron/remote
2、主进程中配置启用 remote 模块
const remote=require(’@electron/remote/main’)
remote.initialize()
remote.enable(mainWindow.webContents);
主进程配置详细代码
const { app, BrowserWindow } = require("electron");
const path = require("path");
//1、引入初始化remote模块
const remote=require('@electron/remote/main');
remote.initialize();
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 1000,
height: 600,
webPreferences: {
nodeIntegration: true, //允许渲染进程使用nodejs
contextIsolation: false //允许渲染进程使用nodejs
}
});
mainWindow.loadFile(path.join(__dirname, "index.html"));
// mainWindow.loadURL("https://www.itying.com")
//打开调试模式
mainWindow.webContents.openDevTools();
//2、启动remote模块
remote.enable(mainWindow.webContents);
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
3、渲染进程引入 remote 模块使用
const { BrowserWindow } = require("@electron/remote");
const path = require("path");
window.onload=()=>{
var btnDom=document.querySelector("#btn")
btnDom.onclick=()=>{
const secondWindow = new BrowserWindow({
width: 300,
height: 200,
});
secondWindow.loadFile(path.join(__dirname, "second.html"));
}
}
6-4、Electron 网页安全政策 Content-Security-Policy
详情参考:https://www.electronjs.org/zh/docs/latest/tutorial/security
Content Security Policy 简称 CSP 是一种网页安全政策
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。
CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
通俗的讲开启 CSP 后可以让浏览器自动禁止外部注入恶意脚本,增加网站的安全性能。
从 2.0 版本开始,如果使用 electron 的开发人员没有定义 Content-Security-Policy,Electron就会在 DevTool console 发出警告提示,如下图:
配置 Content-Security-Policy:
两种方式的规则都是一样的,default-src 代表默认规则,'self'表示限制所有的外部资源,只允许当前域名加载资源。
一般情况下,默认规则可以包揽大多数的需求,但凡事都有例外,资源繁多的时候通常需要特殊配置,最值得关心的是 script 的安全,这至关重要,一旦被注入恶意 script,很多安全控制就会荡然无存,可以使用 script-src 这一指令设置:处。
例如我们要引入 google-analytics 分析流量,可以这样设置:
7、Electron 自定义软件顶部菜单、右键菜单以及绑定快捷键
7-1、Electron 自定义软件顶部菜单以及绑定快捷键
Electron 中 Menu 模块可以用来创建原生菜单,它可用作应用菜单和 context 菜单。
这个模块是一个主进程的模块,并且可以通过 remote 模块给渲染进程调用。
主进程引入menu.js
const { app, BrowserWindow } = require("electron");
const remote=require('@electron/remote/main');
remote.initialize();
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration:true, //开启渲染进程中使用nodejs
contextIsolation:false, //开启渲染进程中使用nodejs
}
});
//启动remote模块
remote.enable(mainWindow.webContents)
//在渲染进程中开启调试模式
mainWindow.webContents.openDevTools()
mainWindow.loadFile(path.join(__dirname, "index.html"));
//引入menu渲染进程
require('./ipcMain/menu');
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
menu.js
const { Menu } = require("electron");
//https://www.electronjs.org/docs/api/menu-item
var menuTemplate=[
{
label:"文件",
submenu:[
{
label:"新建",
accelerator:"ctrl+n",
click:()=>{
console.log("Ctrl+N")
}
},
{
label:"打开",
accelerator:"ctrl+o",
click:()=>{
console.log("Ctrl+O")
}
},
{
type:"separator"
},
{
label:"保存"
}
]
},
{
label:"编辑",
submenu:[
{
label:"复制",
role:"copy",
click:()=>{
console.log("copy")
}
},
{
label:"黏贴",
role:"paste"
}
]
}
];
var menuBuilder=Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menuBuilder);
7-2、Electron 自定义右键菜单
实现右键菜单有两种方法:
1、使用@electron/remote 模块实现
2、使用主进程和渲染进程通信实现
通过@electron/remote 模块实现
渲染进程
const remote = require('@electron/remote')
const Menu=remote.Menu;
var menuContextTemplate=[
{
label:"复制",
role:"copy",
click:()=>{
console.log("copy")
}
},
{
label:"黏贴",
role:"paste"
},
{ type: 'separator' }, //分隔线
{
label:"其他功能",
click:()=>{
console.log("其他功能")
}
}, {
label:"文件",
submenu:[
{
label:"新建",
accelerator:"ctrl+n",
click:()=>{
console.log("Ctrl+N")
}
},
{
label:"打开",
accelerator:"ctrl+o",
click:()=>{
console.log("Ctrl+O")
}
},
{
type:"separator"
},
{
label:"保存"
}
]
},
];
var menuBuilder=Menu.buildFromTemplate(menuContextTemplate);
window.onload=()=>{
window.addEventListener("contextmenu",(e)=>{
console.log("鼠标点击了右键")
e.preventDefault()
menuBuilder.popup({
window:remote.getCurrentWindow()
})
},false)
}
使用主进程和渲染进程通信实现
主进程main.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration:true, //开启渲染进程中使用nodejs
contextIsolation:false, //开启渲染进程中使用nodejs
}
});
//在渲染进程中开启调试模式
mainWindow.webContents.openDevTools()
mainWindow.loadFile(path.join(__dirname, "index.html"));
//自定义顶部菜单
require('./ipcMain/ipcMain');
require('./ipcMain/contextMenu');
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
ipcMain文件夹下ipcMain.js模块
const { ipcMain } = require('electron');
//接收渲染进程的通知
ipcMain.on("sendMsg",(e,data)=>{
console.log(data);
console.log(e);
})
//主进程接收到异步消息以后通知渲染进程
ipcMain.on("sendMsgReplay",(e,data)=>{
console.log(data);
e.sender.send("replayRenderer","收到了消息")
})
//接收同步消息
ipcMain.on("sendMsgReplaySync",(e,data)=>{
console.log(data);
e.returnValue="我是主进程 已经收到了你的消息"
})
ipcMain文件夹下contextMenu.js模块
const { Menu,ipcMain,BrowserWindow } = require("electron");
var contextTemplate = [
{ label: '复制', role: 'copy' },
{ label: '黏贴', role: 'paste' },
{ type: 'separator' },
//分隔线
{ label: '其他功能', click: () => { console.log('click') } }
];
var menuBuilder=Menu.buildFromTemplate(contextTemplate);
//监听右键菜单
ipcMain.on("showContextmenu",()=>{
//渲染进程中获取当前窗口的方法 remote.getCurrentWindow()
//主进程中获取当前窗口的方法 BrowserWindow.getFocusedWindow()
menuBuilder.popup({window:BrowserWindow.getFocusedWindow()});
})
渲染进程ipcRenderer.js模块
const { ipcRenderer } = require('electron');
window.onload=()=>{
var sendMsgDom=document.querySelector("#sendMsg");
sendMsgDom.onclick=()=>{
//渲染进程给主进程发送消息
ipcRenderer.send("sendMsg","this is Renderer msg");
}
//渲染进程给主进程发送消息
var sendMsgReplayDom=document.querySelector("#sendMsgReplay");
sendMsgReplayDom.onclick=()=>{
ipcRenderer.send("sendMsgReplay","this is Renderer msg - sendMsgReplay");
}
//监听主进程的广播
ipcRenderer.on("replayRenderer",(e,data)=>{
console.log(data)
})
//同步发送消息
var sendMsgReplaySyncDom=document.querySelector("#sendMsgSync");
sendMsgReplaySyncDom.onclick=()=>{
var replay= ipcRenderer.sendSync("sendMsgReplaySync","this is Renderer msg - sendMsgReplaySync");
console.log(replay);
}
//监听主进程发送过来的消息
ipcRenderer.on("rendererMsg",(e,data)=>{
console.log(data)
})
//右键菜单
window.addEventListener("contextmenu", (e) => {
e.preventDefault()
//弹出右键菜单 menuBuilder.popup({window:remote.getCurrentWindow()});
ipcRenderer.send("showContextmenu");
}, false)
}
有时候我们想在渲染进程中通过一个事件去执行主进程里面的方法。或者在渲染进程中通知主进程处理事件,主进程处理完成后广播一个事件让渲染进程去处理一些事情。这个时候就
用到了主进程和渲染进程之间的相互通信。
Electron 主进程和渲染进程的通信主要用到两个模块:ipcMain 和 ipcRenderer
ipcMain:当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息, 当然也有可能从主进程向渲染进程发送消息。
ipcRenderer: 使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。
场景 1:渲染进程给主进程发送异步消息:
//渲染进程
const { ipcRenderer } = require('electron')
ipcRenderer.send('msg',{name:'张三'}); //异步
//主进程
const { ipcMain } = require('electron');
ipcMain.on('msg',(event,arg) => {
})
场景 2:渲染进程给主进程发送异步消息,主进程接收到异步消息以后通知渲染进程
//渲染进程
const { ipcRenderer } = require('electron')
ipcRenderer.send('msg',{name:'张三'}); //异步
//主进程
const { ipcMain } = require('electron');
ipcMain.on('msg',(event,arg) => {
event.sender.send('reply', 'pong');
})
//渲染进程
const { ipcRenderer } = require('electron')
ipcRenderer.on('reply', function(event, arg) {
console.log(arg); // prints "pong"
});
场景 3:渲染进程给主进程发送同步消息
//渲染进程
const { ipcRenderer } = require('electron')
const msg = ipcRenderer.sendSync('msg-a');
console.log(msg)
//主进程
ipcMain.on('msg-a',(event)=> {
event.returnValue = 'hello';
})
场景 4:主进程通知渲染进程执行操作
//主进程
BrowserWindow.getFocusedWindow().webContents.send('replay','news');
//渲染进程
const { ipcRenderer } = require('electron')
ipcRenderer.on('reply', function(event, arg) {
console.log(arg); // prints "news"
});
代码:
主进程ipcMain.js模块
const { ipcMain } = require('electron');
//接收渲染进程的通知
ipcMain.on("sendMsg",(e,data)=>{
console.log(data);
console.log(e);
})
//主进程接收到异步消息以后通知渲染进程
ipcMain.on("sendMsgReplay",(e,data)=>{
console.log(data);
e.sender.send("replayRenderer","收到了消息")
})
//接收同步消息
ipcMain.on("sendMsgReplaySync",(e,data)=>{
console.log(data);
e.returnValue="我是主进程 已经收到了你的消息"
})
主进程menu.js模块
const { Menu,BrowserWindow } = require("electron");
//https://www.electronjs.org/docs/api/menu-item
var menuTemplate=[
{
label:"文件",
submenu:[
{
label:"触发渲染进程里面的方法",
click:()=>{
BrowserWindow.getFocusedWindow().webContents.send("rendererMsg","触发渲染进程里面的方法--我是主进程")
}
},
{
label:"新建",
accelerator:"ctrl+n",
click:()=>{
console.log("Ctrl+N")
}
},
{
label:"打开",
accelerator:"ctrl+o",
click:()=>{
console.log("Ctrl+O")
}
},
{
type:"separator"
},
{
label:"保存"
}
]
},
{
label:"编辑",
submenu:[
{
label:"复制",
role:"copy",
click:()=>{
console.log("copy")
}
},
{
label:"黏贴",
role:"paste"
}
]
}
];
var menuBuilder=Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menuBuilder);
主进程main.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration:true, ///开启渲染进程中使用nodejs
contextIsolation:false, //开启渲染进程中使用nodejs
}
});
//在渲染进程中开启调试模式
mainWindow.webContents.openDevTools()
mainWindow.loadFile(path.join(__dirname, "index.html"));
//自定义顶部菜单
require('./ipcMain/ipcMain');
require('./ipcMain/menu');
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
渲染进程
const { ipcRenderer } = require('electron');
window.onload=()=>{
var sendMsgDom=document.querySelector("#sendMsg");
sendMsgDom.onclick=()=>{
//渲染进程给主进程发送消息
ipcRenderer.send("sendMsg","this is Renderer msg");
}
//渲染进程给主进程发送消息
var sendMsgReplayDom=document.querySelector("#sendMsgReplay");
sendMsgReplayDom.onclick=()=>{
ipcRenderer.send("sendMsgReplay","this is Renderer msg - sendMsgReplay");
}
//监听主进程的广播
ipcRenderer.on("replayRenderer",(e,data)=>{
console.log(data)
})
//同步发送消息
var sendMsgReplaySyncDom=document.querySelector("#sendMsgSync");
sendMsgReplaySyncDom.onclick=()=>{
var replay= ipcRenderer.sendSync("sendMsgReplaySync","this is Renderer msg - sendMsgReplaySync");
console.log(replay);
}
//监听主进程发送过来的消息
ipcRenderer.on("rendererMsg",(e,data)=>{
console.log(data)
})
}
9-1、Electron渲染进程通过 localstorage给另一个渲染进程传值
localStorage.setItem(key,value);
localStorage.getItem(key);
渲染进程index.js
const { ipcRenderer } = require("electron");
window.onload = () => {
var btnDom = document.querySelector("#btn");
btnDom.onclick = () => {
//设置一个值
localStorage.setItem("aid",1234);
ipcRenderer.send("openNews")
}
}
主进程ipcMain.js
const { BrowserWindow,ipcMain} = require("electron");
const path = require("path");
ipcMain.on("openNews",()=>{
const mainWindow = new BrowserWindow({
width: 400,
height: 300,
webPreferences: {
nodeIntegration: true, //开启渲染进程中使用nodejs
enableRemoteModule:true //启用Remote模块
}
});
mainWindow.loadFile(path.join(__dirname, "../news.html"));
})
渲染进程news.js
var aid=localStorage.getItem("aid");
console.log(aid)//1234
9-2、通过 BrowserWindow 和 webContents 模块实现渲染进程和渲染进程的通信
webContents 是一个 事件发出者.它负责渲染并控制网页,也是 BrowserWindow 对象的属性。
https://www.electronjs.org/docs/latest/api/web-contents
需要了解的几个知识点:
1、获取当前窗口的 id:
const winId = BrowserWindow.getFocusedWindow().id;
2、监听当前窗口加载完成的事件
win.webContents.on('did-finish-load',(event) => {
})
3、同一窗口之间广播数据
win.webContents.on('did-finish-load',(event) => {
win.webContents.send('msg',winId,'我是 index.html 的数据');
})
4、通过 id 查找窗口
let win = BrowserWindow.fromId(winId);
5、监听当前窗口加载完成的事件触发代码:
渲染进程ipcRenderer文件下index.js模块
const { ipcRenderer } = require("electron");
window.onload = () => {
var btnDom = document.querySelector("#btn");
btnDom.onclick = () => {
ipcRenderer.send("openNews","1234")
}
}
ipcMain主进程
const { BrowserWindow,ipcMain} = require("electron");
const path = require("path");
ipcMain.on("openNews",(event,data)=>{
console.log(data)
const newsWindow = new BrowserWindow({
width: 400,
height: 300,
webPreferences: {
nodeIntegration: true, //开启渲染进程中使用nodejs
contextIsolation:false, //开启渲染进程中使用nodejs
}
});
newsWindow.loadFile(path.join(__dirname, "../news.html"));
newsWindow.webContents.on('did-finish-load',(event) => {
newsWindow.webContents.send("toNews",data);
})
})
渲染进程news.js模块
const { ipcRenderer } = require("electron");
ipcRenderer.on("toNews",(e,data)=>{
console.log(data)
})
6、通过 id 查找窗口代码实现
渲染进程ipcRenderer文件夹下index.js模块
const { ipcRenderer } = require("electron");
window.onload = () => {
var btnDom = document.querySelector("#btn");
btnDom.onclick = () => {
ipcRenderer.send("openNews","1234")
}
}
//接收主进程传过来的值 主进程里面的数据来源于news这个渲染进程
ipcRenderer.on("toIndex",(event,data)=>{
console.log(data)
})
主进程ipcMain模块
const { BrowserWindow,ipcMain} = require("electron");
const path = require("path");
var indexId; //index渲染进程的Id
ipcMain.on("openNews",(event,data)=>{
indexId=BrowserWindow.getFocusedWindow().id;
console.log("indexId:",indexId);
const newsWindow = new BrowserWindow({
width: 400,
height: 300,
webPreferences: {
nodeIntegration: true, //开启渲染进程中使用nodejs
contextIsolation:false, //开启渲染进程中使用nodejs In Electron 12, the default will bechanged to `true
enableRemoteModule:true //启用Remote模块
}
});
newsWindow.loadFile(path.join(__dirname, "../news.html"));
newsWindow.webContents.on('did-finish-load',(event) => {
newsWindow.webContents.send("toNews",data);
})
// var newsId=BrowserWindow.getFocusedWindow().id;
// console.log("newsId:",newsId);
})
ipcMain.on("runIndexFn",(e,data)=>{
console.log(data)
console.log(indexId)
//获取index对应的BrowserWindow
let mainWin = BrowserWindow.fromId(indexId);
mainWin.webContents.send("toIndex",data)
})
渲染进程news.js模块
const { ipcRenderer } = require("electron");
ipcRenderer.on("toNews",(e,data)=>{
console.log(data)
})
window.onload = () => {
var btnDom = document.querySelector("#btn");
btnDom.onclick = () => {
ipcRenderer.send("runIndexFn","this is news html")
}
}
1、Electron shell 模块 在用户默认浏览器中打开URL的示例
https://www.electronjs.org/docs/latest/api/shell
var {shell}=require('electron')
shell.openExternal('https://github.com');
2、Electron 中的 webview
https://www.electronjs.org/docs/latest/api/webview-tag
Electron5.x 之后官方不建议使用 webview 标签,可以使用 iframe 替代 webview
3、窗口加载网页BrowserView
mainWindow.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadURL('https://electronjs.org')
dialog 模块提供了 api 来展示原生的系统对话框,例如打开文件框,alert 框,所以 web 应用可以给用户带来跟系统应用相同的体验。
https://www.electronjs.org/docs/latest/api/dialog
渲染进程dialog.js模块
var remote = require('@electron/remote');
//https://electronjs.org/docs/api/dialog
var errorDom = document.querySelector('#error');
var mesageBoxDom = document.querySelector('#mesageBox');
var openDialogDom = document.querySelector('#openDialog');
var saveDialogDom = document.querySelector('#saveDialog');
var mesageBoxSyncDom = document.querySelector('#mesageBoxSync');
var openDialogSyncDom = document.querySelector('#openDialogSync');
var showSaveDialogSyncDom = document.querySelector('#showSaveDialogSync');
errorDom.onclick = function () {
remote.dialog.showErrorBox("错误!","数据输入错误");
}
mesageBoxDom.onclick = function () {
remote.dialog.showMessageBox({
type:"info",
// buttons:["ok","no"],
buttons:["确定","取消","其他"],
title:"关于我们",
message:`
版本: 1.49.1 (user setup)
提交: 58bb7b2331731bf72587010e943852e13e6fd3cf
日期: 2020-09-16T23:27:51.792Z
Electron: 9.2.1
Chrome: 83.0.4103.122
Node.js: 12.14.1
V8: 8.3.110.13-electron.0
OS: Windows_NT x64 6.1.7601`
}).then((result)=>{
console.log(result.response)
if(result.response==0){
console.log("点击了确定")
}else if(result.response==1){
console.log("点击了取消")
}
}).catch((err)=>{
console.log(err)
})
}
openDialogDom.onclick = function () {
remote.dialog.showOpenDialog({
title:"打开目录",
// properties: ['openFile', 'multiSelections']
// properties: ['openFile']
properties:["openDirectory"]
}).then((result)=>{
console.log(result.canceled)
console.log(result.filePaths)
}).catch((err)=>{
console.log(err)
})
console.log("打开文件...")
}
saveDialogDom.onclick = function () {
remote.dialog.showSaveDialog({
title:"保存文件",
defaultPath:"aaa.txt",
filters: [
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
{ name: 'Custom File Type', extensions: ['as'] },
{ name: 'All Files', extensions: ['*'] }
]
}).then((result)=>{
console.log(result.canceled)
console.log(result.filePath)
}).catch((err)=>{
console.log(err)
})
}
mesageBoxSyncDom.onclick=function(){
var result=remote.dialog.showMessageBoxSync({
type:"info",
buttons:["ok","no"],
title:"关于我们",
message:`
版本: 1.49.1 (user setup)
提交: 58bb7b2331731bf72587010e943852e13e6fd3cf
日期: 2020-09-16T23:27:51.792Z
Electron: 9.2.1
Chrome: 83.0.4103.122
Node.js: 12.14.1
V8: 8.3.110.13-electron.0
OS: Windows_NT x64 6.1.7601`
})
console.log(result);
console.log("111");
}
openDialogSyncDom.onclick=function(){
var result = remote.dialog.showOpenDialogSync({
title:"打开目录",
// properties: ['openFile', 'multiSelections']
// properties: ['openFile']
properties:["openDirectory"]
})
console.log(result);
console.log("执行");
}
showSaveDialogSyncDom.onclick=function(){
var result = remote.dialog.showSaveDialogSync({
title:"保存文件",
defaultPath:"aaa.txt",
filters: [
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
{ name: 'Custom File Type', extensions: ['as'] },
{ name: 'All Files', extensions: ['*'] }
]
})
console.log(result);
console.log("执行");
}
12-1、Electron 创建任务栏图标以及任务栏图标右键菜单
var { Menu, Tray,app,BrowserWindow } = require('electron');
const path = require('path');
var appIcon = new Tray(path.join(__dirname,'lover.png'));
const menu = Menu.buildFromTemplate( [
{
label: '设置', click: function () {} //打开相应页面
},{
label: '帮助', click: function () {}
},{
label: '关于', click: function () {}
},{
label: '退出', click: function () {
//
BrowserWindow.getFocusedWindow().webContents().send('close-main-window');
app.quit();
}
}
])
appIcon.setToolTip('my best app');
appIcon.setContextMenu(menu);
12-2、监听任务栏图标的单击、双击事件
var { Menu, Tray,app,BrowserWindow } = require('electron');
var appIcon = new Tray(path.join(__dirname,'lover.png'));
appIcon.on('double-click',()=>{
console.log(win);
win.show();
})
12-3、Electron 点击右上角关闭按钮隐藏任务栏图标
const win = BrowserWindow.getFocusedWindow();
win.on('close',(e)=>{
console.log(win.isFocused());
if(!win.isFocused()){
win=null;
}else{
e.preventDefault(); /*阻止应用退出*/
win.hide(); /*隐藏当前窗口*/
}
})
12-4、Electron 实现任务栏闪烁图标
timer=setInterval(function() {
count++;
if (count%2 == 0) {
appIcon.setImage(path.join(__dirname,'empty.ico'))
} else {
appIcon.setImage(path.join(__dirname,'lover.png'))
}
}, 500)
代码:
主进程main.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
//注意:把trayIcon 定义到全局
let trayIcon=null;
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
});
mainWindow.loadFile(path.join(__dirname, "index.html"));
require("./ipcMain/Tray");
}
//监听应用的启动事件
app.on("ready", createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Macos 中点击 dock 中的应用图标的时候重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
主进程Tray.js模块
const { Tray, Menu, BrowserWindow, app } = require('electron');
const path = require("path");
//创建系统托盘
//注意:把trayIcon 定义到全局
trayIcon = new Tray(path.join(__dirname, "../static/lover.png"));
//系统托盘的右键菜单
var contextMenu = Menu.buildFromTemplate([
{
label: "系统设置",
click: () => {
console.log("setting")
}
},
{
label: "更新软件",
click: () => {
console.log("update")
}
},
{
label: "退出",
click: () => {
if (process.platform !== 'darwin') {
app.quit();
} else {
app.exit();
}
}
}
])
trayIcon.setToolTip("electron托盘");
trayIcon.setContextMenu(contextMenu);
//点击右上角关闭按钮隐藏任务栏图标
let win = BrowserWindow.getFocusedWindow()
win.on("close", (e) => {
if (!win.isFocused()) {
win = null;
} else {
//阻止默认行为
e.preventDefault();
win.hide();
}
})
//监听托盘双击的事件
trayIcon.on("double-click", () => {
win.show();
})
//实现闪烁图标
let count = 1;
let timer = setInterval(() => {
count++;
if (count % 2 == 0) {
trayIcon.setImage(path.join(__dirname, '../static/empty.ico'))
} else {
trayIcon.setImage(path.join(__dirname, '../static/favicon2.ico'))
}
}, 500)
13-1、Electron 实现消息通知
Electron 里面的消息通知是基于 h5 的通知 api 实现的。
https://developer.mozilla.org/zh-CN/docs/Web/API/notification
const option = {
title: 'title', body: 'body',
icon: path.join('main-process/favicon2.ico')
}
const myNotification = new window.Notification(option.title,option);
myNotification.onclick = () =>{
console.log('clicked');
}
13-2、Electron 监听网络变化
https://www.electronjs.org/zh/docs/latest/tutorial/online-offline-events
渲染进程正使用
//监听网络变化实现通知
window.addEventListener('online', function () {
console.log('有网络了');
})
window.addEventListener('offline', function () {
//其他参数查询Notification文档
var option = {
title: '网易邮箱',
body: '网络异常,请检查您的网络'
}
var myNotification = new window.Notification(option.title, option);
myNotification.onclick = function () {
console.log('点击了');
}
})
14、Electron 注册全局快捷键(globalShortcut) 以及 clipboard 剪切板事件以及 nativeImage 模块
14-1、Electron 注册全局快捷键(globalShortcut)
主进程globalShortcut.js模块
var { globalShortcut, app } = require('electron');
app.on('ready', function () {
//注册全局快捷键
globalShortcut.register('ctrl+e', function () {
console.log('ctrl+e');
})
globalShortcut.register('ctrl+d', function () {
console.log('ctrl+d registed');
})
//检测快捷键是否注册功能
console.log(globalShortcut.isRegistered('ctrl+e'));
})
app.on('will-quit', function () {
//注销全局快捷键的监听
globalShortcut.unregister('ctrl+e');
globalShortcut.unregister('ctrl+d');
})
14-2、clipboard 剪切板事件 clipboard 模块以及 nativeImage模块
主进程clipboard.js模块
//clipboard模块可以在主进程里面使用 也可以在渲染进程里面使用
var { clipboard, nativeImage } = require('electron');
//复制
// clipboard.writeText('机器码')
// 粘贴
// clipboard.readText();
var code = document.querySelector('#code');
var btn = document.querySelector('#btn');
var input = document.querySelector('#input');
code.onclick = function () {
clipboard.writeText(code.innerHTML);
alert('复制成功'); //写一个div提示
}
btn.onclick = function () {
//获取复制的内容
input.value = clipboard.readText();
}
//监听按钮点击复制图片的事件
var btncopyimg = document.querySelector('#btncopyimg');
btncopyimg.onclick = function () {
//复制图片黏贴到我们页面上
/*
1.引入nativeImage
2、创建一个nativeImage的对象
*/
var image = nativeImage.createFromPath('static/favicon2.ico');
//复制图片
clipboard.writeImage(image);
//粘贴图片
var imgsrc = clipboard.readImage().toDataURL();
console.log(imgsrc); //base64的地址
//创建一个img标签 指定他的src
var imgDom = new Image();
imgDom.src = imgsrc;
document.body.appendChild(imgDom);
}
electron-log 模块是一个非常简单的 electorn 日志模块,可以打印日志、保存日志、还可以把软件日志同步到远程服务器,您可以在主进程和渲染器进程中直接使用此模块。log 支持
的日志级别有:error, warn, info, verbose, debug, silly
地址:https://github.com/megahertz/electron-log
15-1、安装
npm install electron-log
15-2、使用
const log = require('electron-log');
log.info('Hello, log');
log.warn('Some problem appears');
15-3、修改本地日志输出目录
日志默认输出目录:
on Linux: ~/.config/{app name}/logs/{process type}.log
on macOS: ~/Library/Logs/{app name}/{process type}.log
on Windows: %USERPROFILE%\AppData\Roaming\{app name}\logs\{process type}.log
设置日志文件的路径:
log.transports.file.file = 'd:/myLog.log'
15-4、关闭日志
log.transports.file.level = false; //关闭文件日志,默认是开启的
log.transports.console.level = false; //关闭控制台日志,默认是开启的
15-5、使用第三方模块 electron-log 把日志发送到远程服务器
参考:https://github.com/megahertz/electron-log/blob/HEAD/docs/remote.md
Electron 中的配置:
log.transports.remote.level = 'warn';
log.transports.remote.url = 'https://example.com/myapp/add-log'
log.warn('Some problem appears', { error: e });
Post 的数据格式:
POST /myapp/add-log HTTP/1.1
Content-Length: 300
Content-Type: application/json
Host: example.com
Connection: close
{ "client": { "name": "electron-application" }, "data": [ "Some problem appears", { "error": { "constructor": "Error", "stack": "Error: test\n at App.createWindow ..."
}}
],"date": 1574238042989, "level": "warn", "variables": { "processType": "browser" }
}
NodeJs Express 后台接收
const express = require('express')
var bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post("/addLog", (req, res) => {
console.log(req.body);
res.send("接收 post 的日志信息")
})
app.listen(3000)