东莞网站设计的公司,郴州市,装修公司十大排名北京哪家好,网站开发需解决的难题简介
Electron集成了包括chromium#xff08;理解为具备chrom浏览器的工具#xff09;#xff0c;nodejs#xff0c;native apis
chromium#xff1a;支持最新特性的浏览器。
nodejs#xff1a;js运行时#xff0c;可实现文件读写等。
native apis #xff1a;提供…简介
Electron集成了包括chromium理解为具备chrom浏览器的工具nodejsnative apis
chromium支持最新特性的浏览器。
nodejsjs运行时可实现文件读写等。
native apis 提供统一的原生界面能力。直接与操作系统通信。例如系统通知快捷键等。底层硬件的参数等。
Electron有一个主进程以及渲染进程启动时首先启动main.js或index.js然后创建一个native ui会生成多个browserWindow用window呈现界面这里就是web界面是在chromium上展示的前端页面此时browserWindow就是渲染进程不同的渲染进程是相互独立的各自运行在自己的沙箱环境当中不同的窗口之间需要数据交互因此electron内部提供了IPC和RPC通信机制。
主进程可以看做是package.json中main属性对应的文件一个应用只会有一个主进程只有主进程可以进行GUI的api操作也就是可以调用native apis。
windows中展示的界面通过渲染进程表现支持dom操作以及node api的调用操作。一个应用可以有多个渲染进程渲染进程也可以和调用native apis但是必须先和主进程建立通信然后由主进程调用。
Electron环境搭建windows
npm init
npm install --save-dev electron
{name: electron,version: 1.0.0,description: ,main: main.js,scripts: {test: echo \Error: no test specified\ exit 1},author: ,license: ISC,devDependencies: {electron: ^26.3.0}
}创建main.js文件
const {app,BrowserWindow} require(electron)//当app启动后执行串口创建等操作
app.whenReady().then((){const mainWin new BrowserWindow({width:600,height:400,})//在当前窗口中加载指定界面让它显示具体的内容mainWin.loadFile(index.html)//退出时的生命周期函数mainWin.on(close,(){console.log(close~~~~~);})
})app.on(window-all-closed,(){console.log(all closed);app.quit()
})
创建index.html
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleelelctron/title
/head
bodyh2自定义桌面应用/h2
/body
/html
electron生命周期
readyapp初始化完成
dom-ready一个窗口的文本加载完成
did-finish-load导航完成时触发
window-all-close所有窗口都被关闭时触发如果没有监听这个事件默认会执行退出操作如果监听了这个事件那么就需要我们自己决定是否将当前的应用程序退出。如果没有写之星退出操作那么之后的三个生命周期都会失效。
before-quit在关闭窗口之前触发。
will-quit在窗口关闭并且应用退出时触发
quit当所有窗口被关闭时触发app
closed当窗口关闭时触发此时应删除窗口引用。
const {app,BrowserWindow} require(electron)//创建窗口
function createWindow(){let mainWin new BrowserWindow({width:800,height:400})mainWin.loadFile(index.html)//控制dom元素的对象mainWin.webContents.on(did-finish-load,(){console.log(3333);})mainWin.webContents.on(dom-ready,(){console.log(2222);})mainWin.on(close,(){console.log(8888this window is closed);//关闭释放mainWin null})
}app.on(ready,(){console.log(1111);createWindow()
})
app.on(window-all-closed,(){console.log(4444);app.quit()
})
app.on(before-quit,(){console.log(5555);
})app.on(will-quit,(){console.log(666);
})app.on(quit,(){console.log(77777);
})
执行顺序1,2,3,8,4,5,6,7
窗口尺寸
首先做一下监听渲染
scripts: {test: echo \Error: no test specified\ exit 1,start: nodemon --watch main.js --exec npm run build,build: chcp 65001 electron . //防止控制台打印乱码},
const {app,BrowserWindow} require(electron)//创建窗口
function createWindow(){let mainWin new BrowserWindow({x:100, //相对于左上角的横坐标设置位置y:100, //相对于左上角的纵坐标设置位置show:false, //内容加载会有延迟设置为false不显示应用width:800,height:400,maxHeight:600,//最大高度maxWidth:1000,//最大宽度minHeight:200,//最小高度minWidth:300,//最小宽度 resizable:false //不能支持缩放操作})mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show,(){mainWin.show()})mainWin.on(close,(){//关闭释放mainWin null})
}app.on(ready,(){createWindow()
})
app.on(window-all-closed,(){app.quit()
})
启动项目
npm start
窗口标题
const {app,BrowserWindow} require(electron)//创建窗口
function createWindow(){let mainWin new BrowserWindow({show:false, //内容加载会有延迟设置为false不显示应用width:800,// transparent:true,//透明窗体height:600,autoHideMenuBar:true, //隐藏菜单条icon:IDisk Globe.ico, //显示图标title:electron, //软件标题在HTML文件不设置title则生效。frame:true //决定显不显示工具栏以及标题})mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show,(){mainWin.show()})mainWin.on(close,(){//关闭释放mainWin null})
}app.on(ready,(){createWindow()
})
app.on(window-all-closed,(){app.quit()
})
创建一个新的窗体
先需要在主进程中创建要注意electron高版本引入remote写法会不一样这里使用的是26
npm install --save electron/remote添加webPreferences属性并且初始化remote
const { app, BrowserWindow } require(electron)
const remote require(electron/remote)//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,// transparent:true,//透明窗体height: 600,autoHideMenuBar: true, //隐藏菜单条icon: IDisk Globe.ico, //显示图标title: electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
}) 渲染进程
const {BrowserWindow} require(electron/remote)
addEventListener(DOMContentLoaded,(){ //dom内容加载完成后触发//点击按钮打开一个新的窗口btn.onclick(){let indexMin new BrowserWindow({width:200,height:200})indexMin.loadFile(list.html)}
})
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidth新窗口, initial-scale1.0titleDocument/title
/head
bodylist
/body
/html
快捷键
control r 渲染html页面
control shift r 打开调试控制台
自定义窗口
实现自定义的最大化最小化以及关闭窗口
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style*{margin: 0;padding: 0;}
.head{display: flex;justify-content: space-between;font-size: 12px;line-height: 16px;border-bottom: 1px solid #c2c2c2;padding: 10px;
}
.head div{display: flex;align-items: center;
}
.head img{width: 16px;height: 16px;margin-right: 10px;
}
/stylebodydiv classheaddivimg src./IDisk Globe.ico altspan自定义窗口/span/divdivimg idminimize src./image/ic_zoom_out_2.png altimg idmax src./image/最大化.png altimg idcloseWindow src./image/关闭.png alt/div/divh2主体内容/h2
/body
script src./index.js/script/html
渲染进程
const { BrowserWindow,getCurrentWindow } require(electron/remote)
addEventListener(DOMContentLoaded, () { //dom内容加载完成后触发let mainWin getCurrentWindow() //获取当前的窗口minimize.onclickfunction (){if(!mainWin.isMaximized()){mainWin.minimize()}}max.onclickfunction (){if(!mainWin.isMaximized()){ //检测是否是最大化mainWin.maximize() //不是则最大化}else{mainWin.restore()//否则回到原始状态}}closeWindow.onclickfunction (){//关闭窗口mainWin.close()}
})
主进程
const { app, BrowserWindow } require(electron)
const remote require(electron/remote/main)//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,// transparent:true,//透明窗体height: 600,autoHideMenuBar: true, //隐藏菜单条icon: IDisk Globe.ico, //显示图标title: electron, //软件标题在HTML文件不设置title则生效。frame: false, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
这只是基础的api调用同时我们也可以加入一些再记得逻辑比如关闭时可以弹出确认框确认按钮关闭否则就不关闭。
onbeforeunloadfunction (){//满足弹窗条件后mainWin.destroy() //继续执行销毁return false}
父子及模态窗口
创建父子关系
parent:getCurrentWindow(), //建立父子关系
modal:true, //模态框子窗口打开时父窗口不可以操作
const subWin new BrowserWindow({parent:getCurrentWindow(), //建立父子关系modal:true, //模态框子窗口打开时父窗口不可以操作width: 400,height: 400})
代码主进程
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title主界面/title
/head
style/stylebodyh2子窗口以及模态窗口/h2button idbtn新增窗口/button
/body
script src./index.js/script/html
const { app, BrowserWindow } require(electron)
const remote require(electron/remote/main)//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,// transparent:true,//透明窗体height: 600,autoHideMenuBar: true, //隐藏菜单条icon: IDisk Globe.ico, //显示图标title: electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
渲染进程
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidth新窗口, initial-scale1.0titlexx窗口/title
/head
bodyh2子窗口/h2
/body
/html
const { BrowserWindow,getCurrentWindow } require(electron/remote)
addEventListener(DOMContentLoaded, () { //dom内容加载完成后触发btn.onclick function () {const subWin new BrowserWindow({parent:getCurrentWindow(), //建立父子关系modal:true, //模态框子窗口打开时父窗口不可以操作width: 400,height: 400})subWin.loadFile(sub.html)}subWin.on(close,(){subWin null})
})
自定义菜单
label菜单名称
submenu二级菜单
click点击事件
type选项之间的分割线
role这里使用about会显示关于electron的版本信息 // 定义自己的菜单let menuTemp [{label:文件,submenu:[{label:打开文件,click(){console.log(打开文件);}},{type:separator},{label:关闭文件},{label:关于,role:about},]},{label:编辑},]// 利用上述模板生成一个菜单let menu Menu.buildFromTemplate(menuTemp)//将上述的自定义菜单添加到应用里Menu.setApplicationMenu(menu)
代码主进程菜单必须可见
const { app, BrowserWindow,Menu } require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title:自定义菜单, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)// 定义自己的菜单let menuTemp [{label:文件,submenu:[{label:打开文件,click(){console.log(打开文件);}},{type:separator},{label:关闭文件},{label:关于,role:about},]},{label:编辑},]// 利用上述模板生成一个菜单let menu Menu.buildFromTemplate(menuTemp)//将上述的自定义菜单添加到应用里Menu.setApplicationMenu(menu)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
菜单角色及类型
//自定义菜单的内容let menuTemp [{label: 角色,submenu: [{ label: 复制, role: copy },{ label: 剪切, role: cut },{ label: 粘贴, role: paste },{ label: 最小化, role: minimize }]},{label: 类型,submenu: [{ label: 选项1, type: checkbox }, //多选框{ label: 选项2, type: checkbox },{ label: 选项3, type: checkbox },{ type: separator },{ label: item1, type: radio },//单选框{ label: item2, type: radio },{ type: separator },{ label: windows, type: submenu, role: windowMenu }, //window的一些操作{label: 其他, submenu: [{ label: 打开,icon:./IDisk Globe.ico,accelerator:ctrl o,click(){ //设置快捷键console.log(open执行);}}]},]},]
动态创建菜单
使用MenuItem可以动态的传入需要的菜单属性实现动态的菜单
main.js
const { app, BrowserWindow, Menu } require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
index.js
const { BrowserWindow,getCurrentWindow ,Menu,MenuItem} require(electron/remote)
let menuItem new Menu()
addEventListener(DOMContentLoaded, () { //dom内容加载完成后触发//自定义全局变量存放菜单项addMenu.onclickfunction(){//创建菜单let menuFile new MenuItem({label:文件,type:normal})let menuEdit new MenuItem({label:编辑,type:normal})let customMenu new MenuItem({label:自定义菜单项,submenu:menuItem})//将自定义菜单添加至menulet menu new Menu()menu.append(menuFile)menu.append(menuEdit)menu.append(customMenu)//放置于app中Menu.setApplicationMenu(menu)}
})addItem.onclick function (){let con menuCon.value.trim()if(con){menuItem.append(new MenuItem({label:con,type:normal}))}menuCon.value null
}
index.html
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2自定义菜单/h2button idaddMenu创建自定义菜单/buttonbrbrinput typetext idmenuConbrbrbutton idaddItem添加菜单项/buttonscript src./index.js/script
/body
/html
右键菜单
主进程同上
//index.js渲染进程
const {Menu,getCurrentWindow} require(electron/remote)const menuTemp [{label:Run Code},{label:转到定义},{label:separator},{label:其他功能,click(){console.log(其他功能选项);}},
]let menu Menu.buildFromTemplate(menuTemp)// 给右击鼠标添加监听事件
addEventListener(DOMContentLoaded,(){addEventListener(contextmenu,ev{ev.preventDefault()menu.popup({window:getCurrentWindow()}) //弹出菜单},false)
})
主进程与渲染进程通信
渲染进程主动发起到主进程
渲染进程使用ipcRender
const {getCurrentWindow} require(electron/remote)
const { ipcRenderer} require(electron)window.onload function (){//采用异步的方式在渲染进程中各主进程发送数据aBtn.onclickfunction (){ipcRenderer.send(msg1,来自渲染进程的异步消息)}//异步接收消息ipcRenderer.on(msg1Re,(ev,data){console.log(data);})//采用同步的方式在渲染进程中各主进程发送数据aBtn2.onclickfunction (){let val ipcRenderer.sendSync(msg2,来自渲染进程的同步消息)console.log(val);}//同步接收消息ipcRenderer.on(msg2Re,(ev,data){console.log(data);})
}
主进程使用ipcMain
const { app, BrowserWindow, Menu, ipcMain } require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})//主进程接收消息
ipcMain.on(msg1,(ev,data){console.log(data);ev.sender.send(msg1Re,这是来自主进程的异步消息)
})ipcMain.on(msg2,(ev,data){console.log(data);ev.returnValue 这是来自主进程的同步消息
})
index.html
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2主进程与渲染进程通信/h2button idaBtn渲染进程到主进程异步操作/buttonbrbrbutton idaBtn2渲染进程到主进程同步操作/button
/body
script src ./index.js/script
/html
主进程主动发起到渲染进程
渲染进程
const {getCurrentWindow} require(electron/remote)
const { ipcRenderer} require(electron)window.onload function (){ipcRenderer.on(mtp,(ev,data){console.log(data);})
}
主进程使用webContents来发送给渲染进程每个窗口都有一个webContents能够控制当前窗口的所有内容
const { app, BrowserWindow, Menu, ipcMain } require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)let temp [{label:send,click(){BrowserWindow.getFocusedWindow().webContents.send(mtp,来自主进程的消息)}}]let menu Menu.buildFromTemplate(temp)Menu.setApplicationMenu(menu)mainWin.loadFile(index.html)mainWin.webContents.openDevTools() //打开开发控制台//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
基于本地存储的渲染进程通信
此处利用localStorage来进行通信
渲染进程一
const {getCurrentWindow} require(electron/remote)
const { ipcRenderer} require(electron)window.onload function (){aBtn.onclickfunction (){ipcRenderer.send(openWindow2)//打开窗口2保存数据至localStoragelocalStorage.setItem(name,test)}
}
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2渲染进程间通信/h2button idaBtn打开窗口2/button
/body
script src ./index.js/script
/html
主进程创建新的窗口
const { app, BrowserWindow, Menu, ipcMain } require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容
// 定义全局变量存放主窗口
let mainWinId null
//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)mainWinId mainWin.id//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}//接受其他进程发送的数据
ipcMain.on(openWindow2,(){let subWin1 new BrowserWindow({parent:BrowserWindow.fromId(mainWinId),width:400,height:300,webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.enable(subWin1.webContents)subWin1.loadFile(subWin1.html)subWin1.on(close,(){subWin1 null})
})app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
渲染进程二
window.onload function(){oInput.value localStorage.getItem(name)
}
渲染进程二对应的html窗口
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidth新窗口, initial-scale1.0titlexx窗口/title
/head
bodyh2子窗口/h2input idoInput typetext
/body
script src./subWin1.js/script
/html
在此处新的窗口和主进程窗口形成父子关系这里采用mainId的属性来确定当前的父窗口。 反过来父窗口给子窗口传数据
父窗口渲染进程index.js
const {getCurrentWindow} require(electron/remote)
const { ipcRenderer} require(electron)window.onload function (){aBtn.onclickfunction (){ipcRenderer.send(openWindow2,来自 index 进程)}ipcRenderer.on(mti,(ev,data){console.log(data);})
}
主进程收到数据后难点在于找到对应的窗体对象通过窗体对象发送数据。在这里是在子窗口加载完毕后发送数据创建的subWin1窗口可以直接使用。父窗口使用mainId来获取。
const { app, BrowserWindow, Menu, ipcMain } require(electron)
const remote require(electron/remote)
console.log(process.platform); //通过操作系统来做跨端兼容
// 定义全局变量存放主窗口
let mainWinId null
//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)mainWin.loadFile(index.html)mainWinId mainWin.id//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}//接受其他进程发送的数据
ipcMain.on(openWindow2,(ev,data){console.log(data);let subWin1 new BrowserWindow({parent:BrowserWindow.fromId(mainWinId),width:400,height:300,webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.enable(subWin1.webContents)subWin1.loadFile(subWin1.html)subWin1.on(close,(){subWin1 null})//此时我们是可以直接拿到sub进程的窗口对象等加载完毕后执行数据发送subWin1.webContents.on(did-finish-load,(){subWin1.webContents.send(its,data)})
})app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})ipcMain.on(stm,(ev,data){//将data经过main进程给到index渲染进程console.log(data);//可以根据指定窗口 id 来获取对应的渲染进程然后执行消息的发送let mainWin BrowserWindow.fromId(mainWinId)mainWin.webContents.send(mti,data)
})
子窗口渲染进程
const {ipcRenderer} require(electron)window.onload function(){oInput.value localStorage.getItem(name)// 发送数据给index.jsbtn.onclickfunction(){ipcRenderer.send(stm,来自于sub的数据)}ipcRenderer.on(its,(ev,data){console.log(data);})
}
Dialog模块
显示用于打开和保存文件、警报等的本机系统对话框。
dialog.showSaveDialogSync
包含以下属性
title string (可选)对话框窗口的标题defaultPath string (可选)对话框的默认展示路径buttonLabel string (可选)「确认」按钮的自定义标签, 当为空时, 将使用默认标签。filters FileFilter[] (可选)可以选择默认展示哪些类型的文件 nameFieldLabel string (可选) macOS 文件名输入框对应的自定义标签名。showsTagField boolean (可选) macOS显示标记输入框, 默认为 true。properties string[] (可选) 包含对话框相关属性。 支持以下属性值: openFile - 允许选择文件 openDirectory - 允许选择文件夹 multiSelections-允许多选。 showHiddenFiles-显示对话框中的隐藏文件。 createDirectory macOS -允许你通过对话框的形式创建新的目录。 promptToCreate Windows-如果输入的文件路径在对话框中不存在, 则提示创建。 这并不是真的在路径上创建一个文件而是允许返回一些不存在的地址交由应用程序去创建。 noResolveAliases macOS-禁用自动的别名路径(符号链接) 解析。 所选别名现在将会返回别名路径而非其目标路径。 treatPackageAsDirectory macOS -将包 (如 .app 文件夹) 视为目录而不是文件。 dontAddToRecent Windows - 不要将正在打开的项目添加到最近的文档列表中。 message string (可选)macOS -显示在输入框上方的消息。securityScopedBookmarks boolean (可选)macOS MAS - 在打包提交到Mac App Store时创建 security scoped bookmarks showOpenDialog返回promise对象所以支持.then以及catch的写法。返回一个对象
{cancel:false //选择文件操作的状态filePath: //选择文件的路径
}const { BrowserWindow, dialog } require(electron/remote)window.onload function () {aBtn.onclick function () {dialog.showOpenDialog({defaultPath: __dirname,buttonLabel: 请选择,title: Electron,properties: [openFile, multiSelections],filters: [{name: 代码文件, extensions: [js, json, html]},{name: 图片文件, extensions: [ico, jpeg, png]},{name: 媒体类型, extensions: [MP4, MP3, avi]},{ name: All Files, extensions: [*] }]}).then(ret{console.log(ret);}).catch(err{})}
}
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2Dialog模块/h2button idaBtn显示对话框/button
/body
script src ./index.js/script
/html showErrorBox
参数
title string - 显示在错误框中的标题.
content string - 显示在错误框中的文本内容.
const { BrowserWindow, dialog } require(electron/remote)window.onload function () {aBtn.onclick function () {dialog.showErrorBox(自定义标题,当前错误内容)}
}
其余还包括
showOpenDialogSyncshowSaveDialogSyncshowMessageBoxSyncshowMessageBoxshowCertificateTrustDialog可参考文档dialog | Electron
showMessageBox可以作为消息提示确认框常用
shell与iframe
shell 模块提供与桌面集成相关的功能。但是要注意因为版本的问题shell要通过remote插件实现引入。
openExternal
通过默认浏览器打开外部网页
const {shell} require(electron/remote)
window.onload function () {aBtn.onclick function () {}btnUrl.addEventListener(click, ev {ev.preventDefault()let urlPath btnUrl.getAttribute(href)shell.openExternal(urlPath)})}
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2Dialog模块/h2button idaBtn打开目录/buttonbrbra idbtnUrl hrefhttps://baidu.com打开url/a
/body
script src ./index.js/script
/html
showItemInFolder
在文件管理器中显示指定的文件。 如果可能请选择文件。
const {shell} require(electron/remote)
const path require(path)
window.onload function () {aBtn.onclick function () {}btnUrl.addEventListener(click, ev {ev.preventDefault()let urlPath btnUrl.getAttribute(href)shell.openExternal(urlPath)})aBtn.addEventListener(click, ev {shell.showItemInFolder(path.resolve(__filename))})
} iframe
使用自定义菜单打开默认浏览器以及窗口中的ifram.src
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2Dialog模块/h2button idaBtn打开目录/buttonbrbra idbtnUrl hrefhttps://baidu.com打开url/aiframe referrerpolicyorigin-when-cross-origin frameborder0 idwebview/iframe/body
script src./index.js/script/html
//main.js
const { app, BrowserWindow ,shell,Menu} require(electron)
const remote require(electron/remote/main)
console.log(process.platform); //通过操作系统来做跨端兼容
// 定义全局变量存放主窗口
let mainWinId null
//创建窗口
function createWindow() {let mainWin new BrowserWindow({show: false, //内容加载会有延迟设置为false不显示应用width: 800,height: 600,icon: IDisk Globe.ico, //显示图标title: Electron, //软件标题在HTML文件不设置title则生效。frame: true, //决定显不显示工具栏以及标题webPreferences: { //设置在渲染分支能够使用node写法nodeIntegration: true,contextIsolation: false,}})//初始化remote模块remote.initialize()remote.enable(mainWin.webContents)let tmp [{label:菜单,submenu: [{label:关于,click(){shell.openExternal(https://baidu.com)}},{label:打开,click(){BrowserWindow.getFocusedWindow().webContents.send(openUrl)}}]}]let menu Menu.buildFromTemplate(tmp)Menu.setApplicationMenu(menu)mainWin.loadFile(index.html)mainWinId mainWin.id//等内容加载准备好展示时改为true显示窗体mainWin.on(ready-to-show, () {mainWin.show()})mainWin.on(close, () {//关闭释放mainWin null})
}app.on(ready, () {createWindow()
})
app.on(window-all-closed, () {app.quit()
})
//index.js
const { ipcRenderer } require(electron)
window.onload function () {ipcRenderer.on(openUrl,(){webview.src https://baidu.com})
}
消息通知Notification
window.onload function () {btn.onclickfunction(){let option {title:electron,body:这是一个消息提示框,ico:./IDisk Globe.ico}let myNotification new window.Notification(option.title,option)myNotification.onclick function (){console.log(点击了页卡);}}
}
全局快捷键
main.js主进程中配置
globalShortcut.register(ctrl q,function(){console.log(快捷键注册成功);
})globalShortcut.unregister(ctrl q)
globalShortcut.unregisterAll() //取消所有的快捷键
app.on(ready, () {//返回true或者falselet ret globalShortcut.register(ctrl q,function(){console.log(快捷键注册成功);})if(!ret){console.log(注册失败);}
})
app.on(will-quit,(){globalShortcut.unregister(ctrl q)globalShortcut.unregisterAll() //取消所有的快捷键
})
剪切板操作
clipboard以及nativeImage的使用
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title/title
/head
style/stylebodyh2剪切板/h2input typetext idainput button idabtn复制/buttonbrbrinput typetext idbinput button idbbtn粘贴/buttonbrbrbutton idgetImage将图片拷贝纸剪切板再粘贴至界面/button
/body
script src./index.js/script/html
const { clipboard,nativeImage } require(electron)
window.onload function () {abtn.onclick function () {clipboard.writeText(ainput.value)}bbtn.onclick function () {binput.value clipboard.readText()}getImage.onclick function () {//将图片放入剪切板时要求图片类型属于nativeImage实例let image nativeImage.createFromPath(./image/ic_zoom_out_2.png)clipboard.writeImage(image)//将剪切板中的图片作为dom元素显示在界面上let image2 clipboard.readImage().toDataURL() //转为base64let imgDom new Image()console.log(imgDom);imgDom.src image2document.body.appendChild(imgDom)}
}
项目初始化
这里以react为例创建react项目脚手架后安装依赖
npm i electron npm i electron-is-dev
package.json添加electron打包用的文件在根目录下创建main.js
main:main.js,
const {app,BrowserWindow} require(electron)
const remote require(electron/remote/main)
const electronIsDev require(electron-is-dev) // 获取但钱环境 开发或生产
let mainWindow app.on(ready,(){mainWindow new BrowserWindow({width:1024,height:650,minWidth:600,webPreferences:{nodeIntegration:true,contextIsolation: false,}})remote.initialize()remote.enable(mainWindow.webContents)const urlLocation electronIsDev?http://localhost:3000:myurlmainWindow.loadURL(urlLocation)
}) 同时添加一个脚本指令
scripts: {start: react-scripts start,build: react-scripts build,test: react-scripts test,eject: react-scripts eject,dev: electron .},
此时需要先启动react项目然后再开一个终端启动electron
npm start //启动react项目npm run dev //启动electron
但此时的方式比较麻烦我们希望可以简化一下需要安装一下插件
npm install concurrently //连接多个命令中间使用空格分开npm i wait-on //等待某个结果执行之后再去执行后续的命令npm i cross-env //跨平台变量设置可以不启动浏览器
scripts: {start: react-scripts start,build: react-scripts build,test: react-scripts test,eject: react-scripts eject,dev: concurrently \cross-env BROWSERnone npm start\ \wait-on http://localhost:3000 electron .\},
启动项目
npm run dev 此处有坑如果使用node环境过高那么将不会唤起electron所以版本不要过高我这里使用版本16。
引入Bootsrap和style-components
npm i bootstrap npm i styled-components
APP.js
import React from react
import styled from styled-components
import bootstrap/dist/css/bootstrap.min.csslet LeftDiv styled.div.attrs({className: col-3 left-panel
})background-color:#7b8c7c;min-height:100vh
let RightDiv styled.div.attrs({className: col-9 right-panel
})
background-color:#c9d8cdfunction App() {return (div classNameApp container-fluid px-0div classNamerow no-guttersLeftDiv左侧/LeftDivRightDiv右侧/RightDiv/div/div);
}export default App;接下来就可以进行react前端项目的开发即可。
数据持久化
使用模块
npm i electron-store
//main.js
const Store require(electron-store)
Store.initRenderer()
//APP.js
const Store window.require(electron-store)
const fileStore new Store({ name: filesInfo }) //创建一个数据管理
fileStore.set(files, storeObj) //存储数据
具体使用方法见项目文件。
关于打包会重新开一篇写。