Electron桌面应用开发基础
Electron桌面应用开发
Electron技术架构
- Chromium 支持最新特性的浏览器
- Node.js Javascript运行时,可实现文件读写
- Native APIS 提供统一的原生界面能力
环境搭建
Node 安装 (我的版本14.15.0)
项目初始化
npm init -y
// 安装Electron
npm i --save-dev electron
// 创建main.js 并在package.json中设置为入口
"main":"main.js"
// 创建index.html 用来书写页面内容
初始化代码
package.json
{
"name": "myElectron",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "nodemon --watch main.js --exec npm run build",
"build": "electron ."
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^24.4.0"
},
"dependencies": {
"@electron/remote": "^2.0.9"
}
}
main.js
const { app, BrowserWindow } = require( 'electron' )
// app 哪个控制应用程序的事件生命周期
// BrowserWindow 创建和管理应用程序Windows
const createWindow = () => {
let mainWin = new BrowserWindow( {
width: 800,
height: 600,
show: false,
backgroundColor: 'aqua',
} )
mainWin.loadFile( 'index.html' )
mainWin.on( 'ready-to-show', () => {
mainWin.show()
} )
mainWin.on( 'close', () => {
mainWin = null
} )
}
app.on( 'ready', () => {
createWindow()
} )
生命周期事件 // 执行顺序如下
ready: app初始化完成
dom-ready: 一个窗口中的文本加载完成
did-finsh-load: 导航完成时触发
closed:当窗口关闭时触发,此时应删除窗口引用
window-all-closed: 所有窗口关闭时触发
before-quit: 在关闭窗口之前触发
will-quit: 在窗口关闭并且应用退出时触发
quit: 当所有窗口被关闭时触发
mainWin.webContents.on( 'did-finish-load', () => {
console.log( '333-did-finish-load' );
} )
// 窗口中文本加载完成
mainWin.webContents.on( 'dom-ready', () => {
console.log( '222dom-ready' );
} )
// 主窗口关闭
mainWin.on( 'closed', () => {
console.log( '88888-事件发生' );
} ) app.on( 'window-all-closed', () => {
console.log( '444-window-all-closed' );
if ( process.platform !== 'darwin' ) app.quit()
} )
app.on( 'ready', () => {
console.log( '111- app初始化完成' );
createWindow()
} ) app.on( 'before-quit', () => {
console.log( '555-before-quit' );
} )
app.on( 'will-quit', () => {
console.log( '666-will-quit' );
} )
app.on( 'quit', () => {
console.log( '777-quit' );
} )
窗口设置
const mainWin = new BrowserWindow( {
x: 100, //x y 窗口开始位置
y: 100,
show: false,
width: 800,
height: 600,
minHeight: 400, // min max 最小最大宽高
minWidth: 50,
maxHeight: 1000,
maxWidth: 1200,
resizable: false, // 窗口是否可调整
minimizable: true,
maximizable: true,
title: '桌面应用',
frame: false,
// autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true, // 运行渲染进程使用node
enableRemoteModule: true,
contextIsolation: false
}
} )
require( '@electron/remote/main' ).initialize()
require( "@electron/remote/main" ).enable( mainWin.webContents )
窗口标题及环境
标题配置
- 优先读取index.html中的title
- index.html中不设置得情况下 可以读取 new BrowserWindow 中配置的title
图标修改
iconframe 是否显示默认导航菜单 + 标题
transparent 设置透明
autoHideMenuBar 是否隐藏 导航菜单
点击打开新窗口
备注: 在main中运行时是主进程 在index.html中运行时是渲染进程
app BrowserWindow 都属于主进程得模块
出于安全考虑 渲染进程中没有办法使用require 可在main中配置
渲染进程不允许直接使用主进程模块 通过remote进行调用
electron 12 之后就已经废除了 remote
// 替换为:
const { BrowserWindow } = require('@electron/remote')
// 在主进程中:
require('@electron/remote/main').initialize()
自定义窗口实现
- 渲染进程中获取 主进程窗口实例
let mainWin = remote.getCurrentWindow() - 获取各个按钮
var btnGroup = document.getElementsByClassName( 'btnGroup' )[0].getElementsByTagName( 'button' ) - 是否最大判断
mainWin.isMaximized() - 最大化
mainWin.maximize()
// 最大化还原
mainWin.restore()
- 最小化
mainWin.minimize()
// <div class="btnGroup">
// <button>最小</button>
// <button>最大</button>
// <button>关闭</button>
// </div>
// 获取按钮组
var btnGroup = document.getElementsByClassName( 'btnGroup' )[0].getElementsByTagName( 'button' )
btnGroup[0].addEventListener( 'click', () => {
if ( !mainWin.isMinimized() ) {
mainWin.minimize()
}
} )
btnGroup[1].addEventListener( 'click', () => {
console.log( '最大化', mainWin.isMaximized() );
if ( !mainWin.isMaximized() ) { // 判断窗口是否最大化
mainWin.maximize() // 最大化
} else {
mainWin.restore()
}
} )
btnGroup[2].addEventListener( 'click', () => {
mainWin.close()
} )
阻止窗口关闭
window.onbeforeunload = function() {
return false
}
动态创建菜单
- 准备模板
let menuArr = [{label:'打开',type:'normal',role:'copy'}]
- 利用模板生成菜单
let menu = Menu.buildFromTemplate( menuArr )
- 添加到应用
Menu.setApplicationMenu( menu )
let temp = [
{
label: 'send',
click () {
BrowserWindow.getFocusedWindow().webContents.send( 'msg2', '主进程发来消息' )
}
}
]
let tem = Menu.buildFromTemplate( temp )
Menu.setApplicationMenu( tem )
// <button id="selfMenu">自定义菜单</button>
// <input type="text" value="" id="inputText">
// <button id="addMenu">加入新增菜单</button>
1. 找到 Menu MenuItem
2. new Menu() 可以将 new MenuItem() 创建的菜单进行添加到菜单栏中
let remote = require( '@electron/remote' )
let Menu = remote.Menu
let MenuItem = remote.MenuItem
window.addEventListener( 'DOMContentLoaded', () => {
// 获取按钮-- 自定义菜单
let selfBtn = document.getElementById( 'selfMenu' )
let inputVal = document.getElementById( 'inputText' )
let addMenu = document.getElementById( 'addMenu' )
let selfMenuItem = new Menu()
selfBtn.addEventListener( 'click', () => {
let menuFile = new MenuItem( { label: '文件', type: 'normal' } )
let menuEdit = new MenuItem( { label: '编辑', type: 'normal' } )
let menuSelf = new MenuItem( { label: '自定义菜单', submenu: selfMenuItem } )
let menu = new Menu()
menu.append( menuFile )
menu.append( menuEdit )
menu.append( menuSelf )
Menu.setApplicationMenu( menu )
} )
addMenu.addEventListener( 'click', () => {
let content = inputVal.value.trim()
if ( content ) {
selfMenuItem.append( new MenuItem( { label: content, type: 'normal' } ) )
content = ''
}
} )
})
右击弹出菜单
创建菜单
监听contextmenu 事件 并阻止默认行为
menu.popup({window:remote.getCurrentWindow()})
// 右键菜单
let rightMenu = [
{
label: 'Run Code',
type: 'normal'
},
{
label: '刷新',
role: 'refresh'
},
{
type: 'separator'
},
{
label: '其他功能',
click () {
console.log( '其他功能已执行' );
}
}
]
let menuRight = Menu.buildFromTemplate( rightMenu )
window.addEventListener( 'contextmenu', ( e ) => {
e.preventDefault()
menuRight.popup( {
window: remote.getCurrentWindow
} )
} )
主进程与渲染进程通信
- ipcRender(on send) ipcMain( on ) 两个进程之间通信
- ipcMain 内部 e.sender.send('xx',xxx)
- ipcMain 内部接收 e.returnValue
BrowserWindow.getFocusedWindow().webContents.send('mtp',来自于主进程的消息) // 依赖按钮之类的事件触发‘
mainWin.contents.openDevtools() // 打开控制台
// main.js
let { app, BrowserWindow, ipcMain } = require( 'electron' )
ipcMain.on( 'msg1', ( e, ev ) => {
console.log( e, ev );
e.sender.send( 'msg2', 666 )
// BrowserWindow.getFocusedWindow().webContents.send( 'msg2', 666 )
} ) // index.js
let remote = require( '@electron/remote' )
let { ipcRenderer } = require( 'electron' )
window.addEventListener( 'DOMContentLoaded', () => {
console.log( 666 );
ipcRenderer.send( 'msg1', '渲染进程发来贺电' )
ipcRenderer.on( 'msg2', ( e, ev ) => {
console.log( e, ev );
} )
} )
localtorage通信
- 获取主窗口id
- BrowserWindow 实例属性 id
- 子窗口设置ID
- BrowserWindow.fromId(mainWin.id)
通信的时候存储 信息到localStorage中
新窗口打开时取值并使用
// main.js
ipcMain.on( 'msg', ( e, data ) => {
if ( data ) {
// 开启第二个窗口
let sub2 = new BrowserWindow( {
parent: BrowserWindow.fromId( mainId ),
width: 300,
height: 150,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false
}
} )
sub2.loadFile( 'sub.html' )
sub2.on( 'close', () => {
sub2 = null
} )
}
} )
// 主进程接收窗口二的信息
ipcMain.on( 'toMain', ( e, data ) => {
// 将数据转发给index进程
let mainOpen = BrowserWindow.fromId( mainId )
mainOpen.webContents.send( 'win2', data )
} ) // index.js
let remote = require( '@electron/remote' )
let { ipcRenderer } = require( 'electron' ) window.addEventListener( 'DOMContentLoaded', () => { // 获取打开窗口按钮
let btnOpen = document.getElementById( 'openTwo' )
btnOpen.addEventListener( 'click', () => {
localStorage.setItem( 'name', '张三' )
ipcRenderer.send( 'msg', true )
} )
ipcRenderer.on( 'win2', ( e, data ) => {
console.log( 'index进程已经接收到', data );
} )
} )
渲染进程之间通信
- 主进程 A 渲染 B渲染
- A给B发生数据 ipcRender.send
- B接收 ipcRender.on
- B给A发数据 先发送给 主进程 主进程再发送给 A
- 注意: 主进程发送时不能直接使用BrowsserWindow.getFocusedWindow().webContents.send('mtp',来自于主进程的消息)
因为当前焦点窗口不一定时主进程窗口
dialog模块
- dialog模块: 主进程模块 在渲染进程中使用remote.diaog 调用对应触发得窗口api
- 配置:
remote.dialog.showOpenDialog( {
defaultPath: __dirname,
buttonLabel: '请选择',
title: '高傲的狼',
properties: ['openFile', 'multiSelections'],
filters: [
{ name: '代码文件', extensions: ['js', 'html', 'json'] },
{ name: '图片文件', extensions: ['ico', 'jpeg', 'png'] },
{ name: '媒体文件', extensions: ['mp3', 'mp4', 'avi'] },
]
} ).then( res => {
console.log( res );
} )
shell 打开url或者路劲
- shell.openExternal(url) 默认浏览器打开
- shell.showItemInFolder() 在桌面引用中打开
消息提示
- option = {
title:'高傲的狼',
body:'你好哇,小家伙',
icon:'./xxx.ico'
}
- window.Notification(
option.title,option
)
快捷键
- 注册
globalShortcut('ctrl + q') // 返回布尔值 true/false
- 判断是否注册过
globalShortcut.isRegistered('ctrl + q')
- 注销快捷键
- 时机 -- 在生命周期 will-quit中进行注销
- globalShortcut.unregister('ctrl + q')
剪切板模块
- clipboard 模块
- writeText
- readText
- 图片复制 粘贴
- let img = nativeImage.createFromPath( './女孩.jpg' )
2. clipboard.writeImage( img )
- let img = nativeImage.createFromPath( './女孩.jpg' )
// 将剪贴版中图片写到DOM中
3. let oimg = clipboard.readImage()
4. let imgDom = new Image()
5. imgDom.src = oimg.toDataURL()
6. document.body.appendChild( imgDom )
Electron桌面应用开发基础的更多相关文章
- Mac 桌面软件开发基础问答
1> Mac OS X平台下的桌面软件是由什么编程语言处理 答: 由Objective-C, swift编程语言处理 2> Mac OS X平台下的桌面软件是由什么框架构建 答: 由Coc ...
- spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发
前言 Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Electron通过将Chromium和Node.js合并到同一个运行时环 ...
- 中文代码示例之Electron桌面应用开发初体验
参考: 打造你的第一个 Electron 应用 首先运行下面在目录下创建package.json: $ npm init 去掉了一些无关项后内容如下: { "name": &quo ...
- 中文代码示例之NW.js桌面应用开发初体验
先看到了NW.js(应该是前身node-webkit的缩写? 觉得该起个更讲究的名字, 如果是NorthWest之意的话, logo(见下)里的指南针好像也没指着西北啊)和Electron的比较文章: ...
- electron-vue:Vue.js 开发 Electron 桌面应用
相信很多同学都知道 Electron 可以帮助开发人员使用前端技术开发桌面客户端应用,今天介绍的 electron-vue 框架是一套基于 Vue.js 开发 Electron 桌面应用的脚手架,该项 ...
- nw.js桌面软件开发系列 第0.1节 HTML5和桌面软件开发的碰撞
第0.1节 HTML5和桌面软件开发的碰撞 当我们谈论桌面软件开发技术的时候,你会想到什么?如果不对技术本身进行更为深入的探讨,在我的世界里,有这么多技术概念可以被罗列出来(请原谅我本质上是一个Win ...
- JavaEE开发基础
1 JavaEE简介 Java平台有三个版本,分别是JavaSE(Java Platform, Standard Edition),JavaEE(Java Platform, Enterprise E ...
- Android 开发基础及环境配置
2011年买了第一部安卓操作系统的手机,当时势头正盛的HTC不可思议(incredible),当时的想法就是想学习下智能手机开发,但是由于各种原因,客观上是公司的项目太忙了,忙于项目管理.团队建设.客 ...
- Web桌面应用框架3:Web桌面应用开发的N种Style
研究Web桌面应用开发有一段时间了,总结了Web桌面应用开发的一些主流方式. 一.前端Style 这种方式的就是直接实现一个Web程序,再封装一个浏览器展示,相当粗暴和有效.著名的框架就是Electr ...
- [No0000138]软件开发基础知识
1. 本文目的 本文目的在于,介绍软件开发的各种基础知识 以实现,看了之后,对于软件开发的很多领域的基础知识有所了解 如此在进行后续的真正的软件开发时,遇到各种细节知识,才会明白由来和背景知识 第 1 ...
随机推荐
- 因为手哆嗦,发现了一个关于Python逗号的隐藏用法
python常规的用法,众多pythoner早已熟烂于心,如: 1.当一个元组只有一个元素时 a = (1, ) 2.当表示解包一个容器时 a = [('amo', 1), ('bmo', 1)] ...
- rocketmq-spring : 实战与源码解析一网打尽
RocketMQ 是大家耳熟能详的消息队列,开源项目 rocketmq-spring 可以帮助开发者在 Spring Boot 项目中快速整合 RocketMQ. 这篇文章会介绍 Spring Boo ...
- 长达 1.7 万字的 explain 关键字指南!
当你的数据里只有几千几万,那么 SQL 优化并不会发挥太大价值,但当你的数据里去到了几百上千万,SQL 优化的价值就体现出来了!因此稍微有些经验的同学都知道,怎么让 MySQL 查询语句又快又好是一件 ...
- python之sys库
sys --- 系统相关的参数和函数 该模块提供了一些变量和函数.这些变量可能被解释器使用,也可能由解释器提供.这些函数会影响解释器.本模块总是可用的. sys.abiflags 在POSIX系统上, ...
- 多进程和多线程,Thread模块 GIL全局解释锁, 进程池与线程池,协程
1.多进程实现TCP服务端并发: import socket from multiprocessing import Process def get_server(): server = socket ...
- Java设计模式 —— 享元模式
14 享元模式 14.1 享元模式概述 Flyweight Pattern: 运用共享技术有效地支持大量细粒度对象的复用. 当系统中存在大量相同或相似的对象时,它通过共享技术实现相同或相似的细粒度对象 ...
- sql lag函数
lag https://spark.apache.org/docs/latest/api/sql/#lag lag(input[, offset[, default]]) OVER (PARTITIO ...
- 浅谈对属性描述符__get__、__set__、__delete__的理解
1.属性描述符的基础介绍 1.1 何为属性描述符? 属性描述符是一种Python语言中的特殊对象,用于定义和控制类属性的行为.属性描述符可以通过定义__get__.__set__.__delete__ ...
- 回车,换行,转义字符“\r”,“\n”是什么关系?
1."回车"这个名词的来历. 关于"回车键"的来历,还得从机械英文打字机说起.在机械英文打字机上,有一个部件叫"字车",每打一个字符(原为单 ...
- 天梯赛L1-027 出租
一.问题描述 下面是新浪微博上曾经很火的一张图: 一时间网上一片求救声,急问这个怎么破.其实这段代码很简单,index数组就是arr数组的下标,index[0]=2 对应 arr[2]=1,index ...