https://www.electronjs.org/

有的时候 样式更新不过来 ctrl + r

调试面板 ctrl + shift + i

一、Electron技术架构


Electron集成了chromium与node.js,将他们整合到一个运行环境中,允许我们使用web技术 html、css、js来构建桌面应用程序,并且能过通过一些与操作系统无关的api 访问windows\macos

  • Chromium:可以理解为浏览器,支持最新特性的浏览器,es6
  • Node.js:javascript 运行时,可实现文件读写等
  • Native APIs: 提供统一的原生界面能力

二、Electron工作流程

主进程

  • 可以看做是 package.json 中 main 属性对应的文件
  • 一个应用只会有一个主进程
  • 只有主进程可以进行 GUI 的 API 操作

渲染进程

  • Windows 中展示的界面通过渲染进程表现
  • 一个应用可以有多个渲染进程

三、Electron工作流程

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 windows is closed')app.quit()
})

四、Electron生命周期

  • ready: app 初始化完成
  • dom-ready: 一个窗口中的文本加载完成
  • did-finsh-load: 导航完成时触发
  • window-all-closed: 所有窗口都被关闭时触发
  • before-quit: 在关闭窗口之前触发
  • will-quit: 在窗口关闭并且应用退出时触发
  • quit: 当所有窗口被关闭时触发
  • closed: 当窗口关闭时触发,此时应删除窗口引用

main.js

const { app, BrowserWindow } = require('electron')// 创建窗口
function createWindow() {let mainWin = new BrowserWindow({width: 800,height: 400})mainWin.loadFile('index.html')mainWin.webContents.on('did-finish-load', () => {console.log('33333--->did-finish-load')})mainWin.webContents.on('dom-ready', () => {console.log('22222--->dom-ready')})mainWin.on('close', () => {console.log('88888--->this window is closed')mainWin = null})
}app.on('ready', () => {console.log('11111----->ready')createWindow()
})app.on('window-all-closed', () => {console.log('44444---->window-all-closed')app.quit()
})app.on('before-quit', () => {console.log('5555->before-quit')
})app.on('will-quit', () => {console.log('66666->will-quit')
})app.on('quit', () => {console.log('777777-quitquit')
})//22222--->dom-ready
//33333--->did-finish-load
//88888--->this window is closed
//44444---->window-all-closed
//5555->before-quit
//66666->will-quit
//777777-quitquit

五、窗口尺寸

每次修改代码都要重新执行 npm start比较麻烦,我们可以在package.json中做如下配置:

  "scripts": {"start": "nodemon --watch main.js --exec npm run build","build": "electron ."},

屏幕出现短暂空白问题,解决:

屏幕宽高边界设置

禁止屏幕缩放属性

六、窗口标题及环境

去掉默认标题 菜单等

透明窗体

保留标题,隐藏菜单


有的时候 样式更新不过来 ctrl + r

调试面板 ctrl + shift + i

渲染进程里面想使用require,要进行相应的配置,不然报错

  let mainWin = new BrowserWindow({icon: 'lg.ico',  // 设置一个图片路径,可以自定义当前应用的显示图标title: "标题",  // 自定义当前应用的显示标题webPreferences: {nodeIntegration: true,enableRemoteModule: true}})

七、自定义窗口

实现窗口最大化、最小化功能

const { remote } = require('electron')window.addEventListener('DOMContentLoaded', () => {// 利用 remote 可以获取当前窗口对象let mainWin = remote.getCurrentWindow()// 获取元素添加点击操作的监听let aBtn = document.getElementsByClassName('windowTool')[0].getElementsByTagName('div')aBtn[0].addEventListener('click', () => {// 当前事件发生后说明需要关闭窗口mainWin.close()})aBtn[1].addEventListener('click', () => {// 这里需要执行的最大化操作console.log(mainWin.isMaximized())if (!mainWin.isMaximized()) {mainWin.maximize()  // 让当前窗口最大化} else {mainWin.restore()  // 回到原始的状态}})aBtn[2].addEventListener('click', () => {// 实现最小化if (!mainWin.isMinimized()) {mainWin.minimize()}})
})

八、阻止窗口关闭

window.onbeforeunload

window.onbeforeunload = function () {let oBox = document.getElementsByClassName('isClose')[0]oBox.style.display = 'block'let yesBtn = oBox.getElementsByTagName('span')[0]let noBtn = oBox.getElementsByTagName('span')[1]yesBtn.addEventListener('click', () => {mainWin.destroy()})noBtn.addEventListener('click', () => {oBox.style.display = 'none'})return false}

九、父子及模态窗口

modal: true 是设置模态窗口 =》父窗口不可以拖动、关闭

const { remote } = require('electron')window.addEventListener('DOMContentLoaded', () => {let oBtn = document.getElementById('btn')oBtn.addEventListener('click', () => {let subWin = new remote.BrowserWindow({parent: remote.getCurrentWindow(),width: 200,height: 200,modal: true})subWin.loadFile('sub.html')subWin.on('close', () => {subWin = null})})
})

十、自定义菜单


type: ‘separator’ 设置菜单之间的分隔线

const {  Menu } = require('electron')// 定义自己需要的菜单项let menuTemp = [{label: '文件',submenu: [{label: '打开文件',click() {console.log('当前需要做的就是打开某一个具体的文件')}},{type: 'separator'},{label: '关闭文件夹'},{label: '关于',role: 'about'}]},{ label: '编辑' }]// 利用上述的模板然后生成一个菜单项let menu = Menu.buildFromTemplate(menuTemp)// 将上述的自定义菜单添加到应用里Menu.setApplicationMenu(menu)

十一、菜单角色及类型

  // 01 自定义菜单的内容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' }]},

{label: '其它',submenu: [{label: '打开',icon: './open.png',accelerator: 'ctrl + o',click() {console.log('open操作执行了')}}]}

十二、动态创建菜单

const { remote } = require('electron')
const Menu = remote.Menu
const MenuItem = remote.MenuItemwindow.addEventListener('DOMContentLoaded', () => {// 获取要应的元素let addMenu = document.getElementById('addMenu')let menuCon = document.getElementById('menuCon')let addItem = document.getElementById('addItem')// 自定义全局变量存放菜单项let menuItem = new Menu()// 生成自定义的菜单addMenu.addEventListener('click', () => {console.log('111')// 创建菜单 let menuFile = new MenuItem({ label: '文件', type: 'normal' })let menuEdit = new MenuItem({ label: '编辑', type: 'normal' })let customMenu = new MenuItem({ label: '自定义菜单项', submenu: menuItem })// 将创建好的自定义菜单添加至 menu let menu = new Menu()menu.append(menuFile)menu.append(menuEdit)menu.append(customMenu)// 将menu 放置于 app 中显示Menu.setApplicationMenu(menu)})// 动态添加菜单项addItem.addEventListener('click', () => {// 获取当前 input 输入框当中的内容let con = menuCon.value.trim()if (con) {menuItem.append(new MenuItem({ label: con, type: 'normal' }))menuCon.value = ''}})
})

十三、自定义右键菜单

const { remote } = require('electron')
console.log(remote)
const Menu = remote.Menu// 定义菜单的内容
let contextTemp = [{ label: 'Run Code' },{ label: '转到定义' },{ type: 'separator' },{label: '其它功能',click() {console.log('其它功能选项被点击了')}},
]// 依据上述的内容来创建 menu
let menu = Menu.buildFromTemplate(contextTemp)// 给鼠标右击添加监听
window.addEventListener('DOMContentLoaded', () => {window.addEventListener('contextmenu', (ev) => {ev.preventDefault()menu.popup({ window: remote.getCurrentWindow() })}, false)
})

十四、主进程与渲染进程通信

渲染进程 index.js

const { ipcRenderer } = require('electron')window.onload = function () {// 获取元素let aBtn = document.getElementsByTagName('button')// 01 采用异步的 API 在渲染进程中给主进程发送消息aBtn[0].addEventListener('click', () => {ipcRenderer.send('msg1', '当前是来自于渲染进程的一条异步消息')})// 02 采用同步的方式完成数据通信aBtn[1].addEventListener('click', () => {let val = ipcRenderer.sendSync('msg2', '同步消息')console.log(val)})// 当前区域是接收消息ipcRenderer.on('msg1Re', (ev, data) => {console.log(data)})ipcRenderer.on('mtp', (ev, data) => {console.log(data)})
}

主进程 main.js

  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()// 主进程接收消息操作
ipcMain.on('msg1', (ev, data) => {console.log(data)ev.sender.send('msg1Re', '这是一条来自于主进程的异步消息')
})ipcMain.on('msg2', (ev, data) => {console.log(data)ev.returnValue = '来自于主进程的同步消息'
})

十五、基于本地存储的渲染进程间通信1


index1.js

const { ipcRenderer } = require('electron')window.onload = function () {// 获取元素let oBtn = document.getElementById('btn')oBtn.addEventListener('click', () => {ipcRenderer.send('openWin2')// 打开窗口2之后,保存数据至....localStorage.setItem('name', '存储内容')})
}

index2.js

window.onload = function () {let oInput = document.getElementById('txt')let val = localStorage.getItem('name')oInput.value = val
}

main.js

parent: BrowserWindow.fromId(mainWinId), 父子窗口设置

const { app, BrowserWindow, ipcMain } = require('electron')// 定义全局变量存放主窗口 Id
let mainWinId = nullconst createWindow = function () {let mainWin = new BrowserWindow({frame: true,show: false,title: '标题',width: 800,height: 600,webPreferences: {nodeIntegration: true,enableRemoteModule: true}})mainWin.loadFile('index.html')mainWinId = mainWin.id// 接收其它进程发送的数据,然后完成后续的逻辑
ipcMain.on('openWin2', () => {// 接收到渲染进程中按钮点击信息之后完成窗口2 的打开let subWin1 = new BrowserWindow({width: 400,height: 300,parent: BrowserWindow.fromId(mainWinId),webPreferences: {nodeIntegration: true,enableRemoteModule: true}})subWin1.loadFile('subWin1.html')subWin1.on('close', () => {subWin1 = null})
})

十六、基于本地存储的渲染进程间通讯2

父子窗口传值,都是通过主线程做为中转站。

sub.js 子渲染进程

const { ipcRenderer } = require('electron')
window.onload = function () {let oInput = document.getElementById('txt')let val = localStorage.getItem('name')oInput.value = val// 在 sub 中发送数据给 index.js let oBtn = document.getElementById('btn')oBtn.addEventListener('click', () => {ipcRenderer.send('stm', '来自于sub进程')})// 接收数据ipcRenderer.on('its', (ev, data) => {console.log(data)})
}

中转站 main.js

const { app, BrowserWindow, ipcMain } = require('electron')// 定义全局变量存放主窗口 Id
let mainWinId = nullconst createWindow = function () {let mainWin = new BrowserWindow({frame: true,show: false,title: '标题',width: 800,height: 600,webPreferences: {nodeIntegration: true,enableRemoteModule: true}})mainWin.loadFile('index.html')mainWinId = mainWin.id// 接收其它进程发送的数据,然后完成后续的逻辑
ipcMain.on('openWin2', (ev, data) => {// 接收到渲染进程中按钮点击信息之后完成窗口2 的打开let subWin1 = new BrowserWindow({width: 400,height: 300,parent: BrowserWindow.fromId(mainWinId),webPreferences: {nodeIntegration: true,enableRemoteModule: true}})subWin1.loadFile('subWin1.html')subWin1.on('close', () => {subWin1 = null})// 此时我们是可以直接拿到 sub 进程的窗口对象,因此我们需要考虑的就是等到它里面的所有内容// 加载完成之后再执行数据发送subWin1.webContents.on('did-finish-load', () => {subWin1.webContents.send('its', data)})
})ipcMain.on('stm', (ev, data) => {// 当前我们需要将 data 经过 main 进程转交给指定的渲染进程// 此时我们可以依据指定的窗口 ID 来获取对应的渲染进程,然后执行消息的发送let mainWin = BrowserWindow.fromId(mainWinId)mainWin.webContents.send('mti', data)
})

index.js 父渲染进程

const { ipcRenderer } = require('electron')window.onload = function () {// 获取元素let oBtn = document.getElementById('btn')oBtn.addEventListener('click', () => {ipcRenderer.send('openWin2', '来自于 index 进程')})// 接收消息ipcRenderer.on('mti', (ev, data) => {console.log(data)})
}

十七、dialog模块

const { remote } = require('electron')window.onload = function () {let oBtn = document.getElementById('btn')let oBtnErr = document.getElementById('btnErr')oBtn.addEventListener('click', () => {remote.dialog.showOpenDialog({defaultPath: __dirname,buttonLabel: '请选择',title: '标题',properties: ['openFile', 'multiSelections'],filters: [{ "name": '代码文件', extensions: ['js', 'json', 'html'] },{ "name": '图片文件', extensions: ['ico', 'jpeg', 'png'] },{ "name": '媒体类型', extensions: ['avi', 'mp4', 'mp3'] }]}).then((ret) => {console.log(ret)})})oBtnErr.addEventListener('click', () => {remote.dialog.showErrorBox('自定义标题', '当前错误内容')})
}

十八、shell与iframe

在用户的默认浏览器中打开 URL 的示例:

const { shell } = require('electron')shell.openExternal('https://github.com')
window.onload = function () {// 1 获取元素 let oBtn1 = document.getElementById('openUrl')let oBtn2 = document.getElementById('openFolder')oBtn1.addEventListener('click', (ev) => {ev.preventDefault()let urlPath = oBtn1.getAttribute('href')shell.openExternal(urlPath)})oBtn2.addEventListener('click', (ev) => {shell.showItemInFolder(path.resolve(__filename)) // 打开一个目录})
}
<iframe src="https://kaiwu.lagou.com/" frameborder="0" id="webview"></iframe>

在菜单里面打开一个页面

let tmp = [{label: '菜单',submenu: [{label: '关于',click() {shell.openExternal('https://kaiwu.lagou.com/')}},{label: '打开',click() {BrowserWindow.getFocusedWindow().webContents.send('openUrl')}},]}]let menu = Menu.buildFromTemplate(tmp)Menu.setApplicationMenu(menu)
ipcRenderer.on('openUrl', () => {let iframe = document.getElementById('webview')iframe.src = 'https://www.lagou.com/'
})

十九、消息通知

window.onload = function () {let oBtn = document.getElementById('btn')oBtn.addEventListener('click', () => {let option = {title: '标题',body: '描述',icon: './msg.png'}let myNotification = new window.Notification(option.title, option)myNotification.onclick = function () {console.log('点击了消息页卡')}})
}

二十、快捷键注册

app.on('ready', () => {// 注册let ret = globalShortcut.register('ctrl + q', () => {console.log('快捷键注册成功')})if (!ret) {console.log('注册失败')}console.log(globalShortcut.isRegistered('ctrl + q'))console.log(ret, '~~~~~')})app.on('will-quit', () => {console.log(666)globalShortcut.unregister('ctrl + q')  // 取消注册globalShortcut.unregisterAll()  // 全部取消
})

二十一、剪切板模块

const { clipboard, nativeImage } = require('electron')window.onload = function () {// 获取元素let aBtn = document.getElementsByTagName('button')let aInput = document.getElementsByTagName('input')let oBtn = document.getElementById('clipImg')let ret = nullaBtn[0].onclick = function () {// 复制内容ret = clipboard.writeText(aInput[0].value)}aBtn[1].onclick = function () {// 粘贴内容aInput[1].value = clipboard.readText(ret)}oBtn.onclick = function () {// 将图片放置于剪切板当中的时候要求图片类型属于 nativeImage 实例let oImage = nativeImage.createFromPath('./msg.png')clipboard.writeImage(oImage)// 将剪切板中的图片做为 DOM 元素显示在界面上let oImg = clipboard.readImage()let oImgDom = new Image()oImgDom.src = oImg.toDataURL()document.body.appendChild(oImgDom)}
}

Electron桌面应用开发相关推荐

  1. spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发

     前言 Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Electron通过将Chromium和Node.js合并到同一个运行时环 ...

  2. Electron桌面端开发(进程)

    Electron中的进程 进程 前言

  3. 跨平台桌面应用开发工具Electron v11.0.4

    介绍: Electron是GitHub发布的跨平台桌面应用开发工具,支持Web技术开发桌面应用,其本身是基于C++开发的,GUI核心来自于Chrome,而JavaScript引擎使用v8. v11.0 ...

  4. java桌面端开发为什么没就行起来,大部分人选qt,winform,electron?

    java桌面端开发为什么没就行起来的主要原因是基于Java开发的windows桌面端软件的安装部署运行的不便,绝大多数的windows电脑没有安装Java运行环境,并且基于不同版本Java开发的软件必 ...

  5. 前端实战:electron+vue3+ts开发桌面端便签应用

    前端时间我的一个朋友为了快速熟悉 Vue3 开发, 特意使用 electron+vue3+ts 开发了一个桌面端应用, 并在 github 上开源了, 接下来我就带大家一起了解一下这个项目, 在文章末 ...

  6. 如何使用Vue开发Electron桌面程序

    目录 一.Electron介绍 二.项目搭建 1. 使用 vue cli 创建vue项目 2. 安装插件 vue-cli-plugin-electron-builder ​编辑 三.开发总结 1. 配 ...

  7. vue 拖动 datatransfer 问题_electron-vue跨平台桌面应用开发实战教程(四)——窗口样式amp;打开新窗口...

    > 本文我们讲解下怎么修改窗口样式,系统默认的窗口非常普通,通常与设计不符,所以我们要自定义,接下来我们讲解下怎么去掉原有样式,怎么实现实现窗口的最小化,最大化和关闭按钮.还有怎么打开一个新页 ...

  8. electron 桌面程序_如何使用Electron使用JavaScript构建您的第一个桌面应用程序

    electron 桌面程序 by Carol-Theodor Pelu 通过Carol-Theodor Pelu 如何使用Electron使用JavaScript构建您的第一个桌面应用程序 (How ...

  9. python eel 无边框_荐帮你解剖Python的一个轻量级桌面GUI开发第三方库:Eel,让它体无完肤...

    一.Eel介绍 Eel是一个轻量的python桌面GUI开发第三方库. Eel实际上是启动了一个本地的web服务器, 它允许你将python的函数暴露给javascript, 所以网页端也能调用pyt ...

最新文章

  1. Angew. Chem. Int. Ed. | 分子机器学习是合成化学的未来吗?
  2. 【直播回放】100分钟全面剖析图像分割任务,学习CV必知
  3. linux下基于lvs-dr模式的电子商务平台搭建
  4. php 长微博程序,长微博生成工具PHP源码 根据文字生成长微博图片 - substr_count
  5. 表达式求值及转换算法
  6. 论文速递 | 华为开源自研算法Disout | 中科院计算所GCN中文综述
  7. 使用XML及XSL生成简单HTML
  8. Hibernate 与触发器协同工作
  9. 红帽学习笔记[RHCSA] 第五课[用户、权限相关]
  10. 自己用纯Java写的这款Mysql客户端工具美哭了,求收藏~
  11. 为什么视频无法播放,视频无法播放的原因是什么
  12. android 4.4 java版本_Android4.0-4.4 加入实体按键振动支持的方法(java + smali版本号)
  13. MyRolan (快速启动小工具)
  14. 中外互免签证协定一览表(普通护照与公务普通护照)
  15. PAT_乙级_1003_筱筱
  16. EXCEL的扩展名xls与xlsm有啥区别
  17. 直方图匹配(直方图规定化)
  18. pushstate 和 popstate的用法详解
  19. P1244 [NOI2000] 青蛙过河(普及-)
  20. 密码学——elgama加解密及数字签名算法

热门文章

  1. vue路由与动态路由
  2. 超详细、零基础安装纯净的原版Windows 10系统
  3. B 站又上热搜了, HR 称「核心用户都是 Loser」
  4. 自媒体美食类领域发展前景如何?如何做好美食类账号?
  5. 【Python爬虫】最近想买电脑,用Python爬取京东评论做个参考
  6. 打造生产数据闭环,提高质量管控能力
  7. vue导出分页表格到Excel中
  8. 2021年中式烹调师(中级)考试及中式烹调师(中级)试题及解析
  9. jq分页 不刷新页面_jquery无刷新分页
  10. jquery学习之tab切换及动画效果,涉及animate(),siblings()