https://www.electronjs.org/
有的时候 样式更新不过来 ctrl + r
调试面板 ctrl + shift + i
Electron集成了chromium与node.js,将他们整合到一个运行环境中,允许我们使用web技术 html、css、js来构建桌面应用程序,并且能过通过一些与操作系统无关的api 访问windows\macos
主进程
渲染进程
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()
})
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 + 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
})
})
})
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.MenuItem
window.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 = '来自于主进程的同步消息'
})
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 = null
const 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
})
})
父子窗口传值,都是通过主线程做为中转站。
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 = null
const 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)
})
}
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('自定义标题', '当前错误内容')
})
}
在用户的默认浏览器中打开 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)) // 打开一个目录
})
}
在菜单里面打开一个页面
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 = null
aBtn[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)
}
}