nodejs的GUI基础electron(2)
electron 的深入
渲染进程 主进程
渲染进程是一个 chroume 浏览器窗口,是在一个沙盒环境中,不允许接触电脑的原生资源,因此使用 nodejs 主进程中操作电脑的原生资源。
实现一个简单的文件读取
# 主进程
const electron = require('electron')
const path = require('path')
const app = electron.app
const Browerwindow = electron.BrowserWindow
let win = null //把窗口挂载到全局避免JS 垃圾回收机制把他回收掉
app.on('ready', () => {
win = new Browerwindow({
width: 600,
height: 400
})
// win.loadFile('./index.html')
win.loadURL(path.join('file:', __dirname, 'index.html'))
win.on('closed', () => {
win = null
app.quit()
})
win.webContents.openDevTools()
})
app.on('error', () => {})
app.on('window-all-closed', () => {
app.quit()
})
# html
<body>
<h1>
hello electron
</h1>
<button id="btn">
获取package.json
</button>
<textarea name="" id="text" cols="30" rows="10"></textarea>
</body>
<script src="./renderer/index.js">
</script>
# renderer/index.js
const fs = require('fs')
window.onload = function () {
const button = document.getElementById('btn')
const testarea = document.getElementById('text')
button.onclick = () => {
fs.readFile('package.json', (err, data) => {//注意路径 只能是一个绝对路径 path.relative(__dirname, "package.json")
if (err) alert(err)
testarea.innerText = data.toString()
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
实现一个简单的文件拖拽打开
# html
<body>
<div class="content" id="content"></div>
</body>
<script src="./renderer/index.js">
# renderer/index.js 中
const fs = require('fs')
const path = require('path')
window.onload = function () {
const content = document.getElementById('content')
content.ondragenter = content.ondragover = content.ondragleave = function () {//阻止其他拖拽有关的事件
return false //阻止默认行为
}
content.ondrop = (e) => {//拖拽的撒手事件
e.preventDefault()
console.log(e.dataTransfer.files[0])
/**
* File(6218) {
name: "732文章.md",
path: "C:\Users\Administrator\Desktop\732文章.md",
lastModified: 1553637138346,
lastModifiedDate: Wed Mar 27 2019 10: 52: 18 GMT + 1300(新西兰夏令时间),
webkitRelativePath: "",
…
}
lastModified: 1553637138346
lastModifiedDate: Wed Mar 27 2019 10: 52: 18 GMT + 1300(新西兰夏令时间)
__proto__: Object
name: "732文章.md"
path: "C:\Users\Administrator\Desktop\732文章.md"
size: 6218
type: ""
webkitRelativePath: ""
*/
const filePath = e.dataTransfer.files[0].path//获取路径
const size = e.dataTransfer.files[0].size//获取大小
if (size > 10000) return false//文件过大不给打开
fs.readFile(filePath, (err, data) => {
console.log(data)
content.innerHTML = data.toString('utf8')
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
主要的模块
主进程中的模块
app 模块 主进程 控制应用的生命周期 autoupdater 模块 更新 browserWindow 模块 渲染 窗口相关 contentTracing 模块 dialog 模块 弹窗对话 globalshortcut ipcmain 进程通信 menu 菜单 menuitem 菜单单元按钮 powermonitor powersaveblocker 省电处理 protocol session webcontents 相当于上下文 window 对象 tray locales
渲染进程 desktopcapturer ipcrenderer 通信模块的渲染端 remote webframe
都可以使用的
clipboard 键盘 crashreporter 刷新请求 nativeimage 本地图片 screen 屏幕 shell 命令
remote 模块
remote 提供类在渲染进程和主进程之间进程通信的简便途径 也就是在渲染进程在操作主进程的一些模块 点击按钮打开新窗口
var Browserwindow = require('electron').remote.BrowserWindow
let win = null
btn.onclick = () => {
//调用browserwindow 打开新窗口
//渲染进程没办法打开主进程的模块
win = new Browserwindow({
width: 400,
height: 300,
frame: false
// fullscreen: true
})
win.loadURL('http://www.baidu.com')
win.on('closed', () => {
win = null
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
自定义菜单
menu 模块 和 menuitem 木块
顶部菜单
# /main/menu.js
const { Menu } = require('electron')
var template = [
{
label: '文件',
submenu: [
{
label: '新建文件',
click: function() {
//绑定事件
//绑定事件
console.log('新建文件')
},
accelerator: 'ctrl+n' //绑定快捷键
},
{
label: '新建窗口',
click: function() {}
}
]
},
{
label: '编辑',
submenu: [
{
label: '复制',
role: 'copy' //角色事件 复制
},
{
label: '剪切',
role: 'cut' //角色事件
}
]
}
]
const m = Menu.buildFromTemplate(template) //使用template 创造菜单
Menu.setApplicationMenu(m) //菜单的生成
# main.js
//在app.on(ready)引入 上面的js文件
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
在渲染进程中展现菜单
const { Menu } = require('electron').remote
var template = [
{
label: '文件',
submenu: [
{
label: '新建文件',
click: function() {
//绑定事件
//绑定事件
console.log('新建文件')
},
accelerator: 'ctrl+n' //绑定快捷键
},
{
label: '新建窗口',
click: function() {}
}
]
},
{
label: '编辑',
submenu: [
{
label: '复制',
role: 'copy' //角色事件 复制
},
{
label: '剪切',
role: 'cut' //角色事件
}
]
}
]
const m = Menu.buildFromTemplate(template) //使用template 创造菜单
Menu.setApplicationMenu(m) //菜单的生成
# 在html 中引入 上面的JS 文件
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
右键菜单
const menu = remote.Menu
const template = require(path.resolve(__dirname, 'menu-template.js')) //这里的路径要注意
const m = menu.buildFromTemplate(template)
window.addEventListener(
'contextmenu', //监听右键事件
e => {
e.preventDefault()
m.popup({
//创建一个弹出窗口 定义菜单的模板
window: remote.getCurrentWindow()
})
},
false
)
2
3
4
5
6
7
8
9
10
11
12
13
14
菜单项
click Function (可选) - 当菜单项被点击后,将会调用 click(menuItem, browserWindow, event) 。 browserWindow BrowserWindow event Event role String (可选)-内置事件, 定义菜单项的行为, 当指定 click 属性时将被忽略 。 typeString (可选)-可以是 normal、separator、submenu、checkbox 或 radio。普通 分割线 子菜单 多选 单选 label String (可选) 名称 sublabel String (可选) 子名称 accelerator Accelerator (可选) 快捷键 icon (NativeImage | String) (可选) 图标 enabled Boolean (可选) - 如果为 false,该菜单项将会置灰且不可点击。 visibleBoolean (可选)-如果为 false, 该菜单项将完全隐藏。 checkedBoolean (可选)-只应为 checkbox 或 radio 类型菜单项指定。是否被默认选中 registerAccelerator Boolean (可选) - 如果为 false, accelerator 不会被系统注册, 但仍然会被显示. 默认住为 true。 submenu (MenuItemConstructorOptions[] | Menu) (optional) 子菜单 idString (可选)-在单个菜单中是唯一的。如果定义, 则可以通过它来引用该项。
role 对应的菜单有 toggledevtools - 在当前窗口中隐藏/显示开发者工具。 toggleFullScreen - Toggle full screen mode on the current window. resetzoom - 将主页的缩放级别重置为初始大小. zoomin - 主页面放大 10%. zoomout -主页面缩小 10%. editMenu-默认的 "编辑" 菜单 (包括撤消、复制等) windowMenu-默认 "窗口" 菜单 (包括最小化、关闭等)
进程之间的通信
主进程于渲染进程
ipcRenderer 和 ipcMain 之间的通信
使用 ipcRenderer.send('事件名称',数据) 和 ipcMain.on('事件名称',function(event,data){ data 对应上面的数据 event 是一个事件对象 对应整个 ipcRenderer event.sender.send('新的事件名称',数据) })
ipcRenderer.on('新的事件名称',function(data){ 从主进程传递过来的数据 }) ipcRenderer.sendSync 发数据的同步模式 可以进行同步数据广播
渲染进程与渲染进程
通过 loacalstorage 多个渲染进程是在同域下的
对于比较保密的数据 渲染进程域渲染进程之间的通信
ipcrenderer.send('发送','data')
在主进程
ipcmain.on('发送',function(event,data){
...接受到 data
})
广播给其他窗口 win //需要被管广播的 browerwindow 对象
win.webcontents.on('did-finish-load',function(){
win.webContents.send('事件名称','winID','数据') 这个方法要在其他窗口加载完成后才能使用 })
在其他窗口中通过 IPCrenderer 就可以接受到上面的广播数据
渲染进程的双向通信
在每个 browerwindow 对象产生的时候都纪录下 渲染进程对象的 ID 同时在广播的时候传递给 被传播对象,被传播对象通过参数拿到 ID 后 通过方法拿到 browerwindow 对象然后传递
具体实现
# 在渲染进程1 中
ipcRenderer.send('数据流1','haha')
# 在主进程中
//前面有串口1 以及app 初始化 等等省略掉
const bw1ID = BrowserWindow.getFocusedWindow().id
let bw2=null
ipcMainon('数据流1',function(event,data){
if(data=="haha"){
bw2 = new Browserwindow({
width:800,
height:600
})
bw2.webContents.on('did-finish-load',function(){
//在这里把bw1 窗口的ID 传递过去
bw2.webContent.send('数据流2',"hahaha2",bw1ID)
})
}
})
# 在渲染进程2 中
ipcRenderer.on('数据流2',function(event,data,winid){
console.log(data)//data 就是 页面一 通过主进程传递过来的数据
const bw1 =BrowserWindow.fromId(winid)//通过 winid 获取页面1 对象
bw1.webContents.send('数据流3','hahaha3')
})
# 在渲染进程 1 中
ipcRenderer.on('数据流3',function(event,data){
console.log(data==="hahaha3")//true
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
渲染进程 1 传递数据======》主进程(获取数据,获取原窗口 ID,在新窗口完全加载后通过 webcontent 发送数据)====》渲染进程 2 接受数据 和渲染进程 1 的 id(通过 fromid 拿到渲染进程 1 对象 发送事件)====》在渲染进程 1 中通过 ipcrenderer 接受数据
shell 模块
使用默认应用程序管理文件和 url 地址 这个模块既可以用在主进程也可以用在渲染进程中
把连接地址在外部浏览器中打开
# 在html 中有a标签
<a href="http://www.baidu.com" id="baidu"></a>
# 在渲染进程 中
const {shell} = reuqire('electron')
document.querySelector("#baidu").onclick=(e)=>{
e.preventDefault()//阻止浏览器打开的默认事件
shell.openExternal(e.target.href)//在外部中打开
}
2
3
4
5
6
7
8
9
10
11
12
shell 中有几个方法
1 文件以及文件夹操作的方法
shell.showItemInFolder(fullPath) 在文件管理器中显示给定的文件。 也就是打开文件夹 绝对路径 shell.openItem(fullPath) 打开文件 绝对路径 shell.moveItemToTrash(fullPath) 删除到垃圾箱 shell.beep() 播放哔哔声 也就是错误提示音 shell.writeShortcutLink(shortcutPath[, operation], options) 创建一个地址链接 也就是快捷方式等等 shell.readShortcutLink(shortcutPath) 解析快捷方式 看看有没有
webview
webview 和 iframe 类似但是 webview 和渲染进程不在同一个进程,保证了渲染进程的安全, 并且应用和嵌入的内容交互完全都是异步的保证了安全性不受其影响
# 格式
<webview id="webview" src="http://baidu.com" style="position:fixed;width:100%;height:100%" />
2
简单实现菜单的内部打开和外部打开
# 在html 中
<body>
<webview id="webview" src="http://baidu.com" style="position:fixed;width:100%;height:100%">
</webview>
</body>
<script src="./renderer/index.js">
</script>
# 在创建menu的时候
const {
shell,
BrowserWindow
} = require('electron')
const loader = (url) => { //在外部打开
shell.openExternal(url)
}
const load = (url) => { //在内部打开
const win = BrowserWindow.getFocusedWindow()
win.webContents.send('openinset', url)
}
module.exports = [{
label: '内部打开',
submenu: [{
label: '优酷',
click: () => {
load('http://www.youku.com')
}
}, {
label: 'aiqiyi',
click: () => {
load('http://www.aiqiyi.com')
}
}]
}, {
label: '外部打开',
submenu: [{
label: 'baidu',
click: () => {
loader('http://www.baidu.com')
}
}, {
label: 'bilibili',
click: () => {
loader('http://www.bilibili.com')
}
}]
}]
# 在主进程引入菜单的地方
const {
Menu
} = require('electron')
const linkmenu = require('../linkmenutest')
const m = Menu.buildFromTemplate(linkmenu)
Menu.setApplicationMenu(m)
# 在 渲染进程
window.onload = function () {
ipcRenderer.on('openinset', (event, data) => {
document.querySelector('#webview').src = data
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
dialog 模块
调用电脑上的提示信息,打开电脑上的文件目录功能。
引入
#1 在渲染进程中
const { ipcRenderer, shell, remote } = require('electron')
const dialog = remote.dialog
#2 在主进程中
const {dialog} = require('electron') //使用这种dialog 的时候需要在渲染进程与主进程之间使用通信
2
3
4
5
使用
window.onload = function () {
// ipcRenderer.on('openinset', (event, data) => {
// document.querySelector('#webview').src = data
// })
document.querySelector('#error').onclick = () => {//错误提示
dialog.showErrorBox('Error', '出错拉')
}
document.querySelector('#message').onclick = () => {//信息提示以及信息交互
dialog.showMessageBox({
type: 'info',
title: '内同',
message: '提示信息',
buttons: ['确定', '取消']
}, (index) => {
console.log(index) //index 的索引值对应上面点击的按钮
})
}
document.querySelector('#open').onclick = () => {//打开文件夹
dialog.showOpenDialog({
properties: ['openDirectory', 'showHiddenFiles']
}, (data) => {
//在这里执行nodejs 解析的方法
console.log(data)
})
}
document.querySelector('#save').onclick = () => {//保存文件
dialog.showSaveDialog({
title: 'save file',
// defaultPath: "E:\下载",
filters: [{//保存文件允许的种类
name: 'Images',
extensions: ['jpg', 'png', 'gif']
},
{
name: 'Movies',
extensions: ['mkv', 'avi', 'mp4']
},
{
name: 'Custom File Type',
extensions: ['as']
},
{
name: 'All Files',
extensions: ['*']
}
]
}, function (path) {//获取到要保存的路径
console.log(path)//在这里执行nodejs 写出的方法
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50