NW.js
和 Electron
均可以用前端的知识来开发桌面应用。NW.js
和 Electron
起初是同一 个做者开发。后来种种缘由分为两个产品。一个命名为 NW.js
(英特尔公司提供技术支持)、 另外一命名为 Electron
(Github 公司提供技术支持)。NW.js
和 Electron
能够用 Nodejs
中几乎全部的模块。NW.js
和 Electron
不只能够把 html
写的 web
页面打包成跨平台能够安装到电脑上面的软件,也能够经过 javascript
访问操做 系统原生的 UI
和 Api
(控制窗口、添加菜单项目、托盘应用菜单、读写文件、访问剪贴板)。
github
的atom
编辑器、微软的vscode
编辑器,包括阿里内部的一些 软件也是用electron
开发的javascript
1. Electron 是由谁开发的?css
Electron
是由Github
开发html
2. Electron 是什么?前端
Electron
是一个用HTML
,CSS
和JavaScript
来构建跨平台桌面应用程序的一个开源库vue
3. Electron 把 HTML,CSS 和 JavaScript 组合的程序构建为跨平台桌面应用程序的原理 是什么?java
原理为
Electron
经过将Chromium
和Node.js
合并到同一个运行时环境中,并将其打包为Mac
,Windows
和Linux
系统下的应用来实现这一目的。node
4. Electron 什么时候出现的,为何会出现?webpack
Electron
于2013
年做为构建Atom
的框架而被开发出来。这两个项目在2014
春季开源。 (Atom:为 Github 上可编程的文本编辑器)ios
一些历史:git
2013
年 4
月 Atom Shell
项目启动 。2014
年 5
月 Atom Shell
被开源 。2015
年 4
月 Atom Shell
被重命名为 Electron
2016
年 5
月 Electron
发布了 v1.0.0
版本5. Electron 当前流行程度?
目前
Electron
已成为开源开发者、初创企业和老牌公司经常使用的开发工具。
6. Electron 当前由那些人在维护支持?
Electron
当前由Github
上的一支团队和一群活跃的贡献者维护。有些贡献者是独立开发者,有些则在用Electron
构建应用的大型公司里工做。
7. Electron 新版本多久发布一次?
Electron
的版本发布至关频繁。每当Chromium
、Node.js
有重要的bug
修复,新API
或是版本更新时Electron
会发布新版本。
Chromium
发行新的稳定版后的一到两周以内,Electron
中 Chromium
的版本会对其进行更新,具体时间根据升级所需的工做量而定。Node.js
发行新的稳定版一个月后,Electron
中 Node.js
的版本会对其进行更新,具 体时间根据升级所需的工做量而定。8. Electron 的核心理念是什么?
Electron
的核心理念是:保持Electron
的体积小和可持续性开发。
如:为了保持Electron
的小巧 (文件体积) 和可持续性开发 (以防依赖库和API
的泛滥) ,Electron
限制了所使用的核心项目的数量。
好比Electron
只用了Chromium
的渲染库而不是其所有组件。这使得升级Chromium
更加容易,但也意味着Electron
缺乏了Google Chrome
里的一些浏览器相关的特性。 添加到Electron
的新功能应该主要是原生API
。 若是能够的话,一个功能应该尽量的成 为一个Node.js
模块。
9. Electron 当前的最新版本为多少?
Electron
当前的最新版本为4.0.1
(当前时间为2019
年1
月6
号)
1. 安装 electron
npm install -g electron
2. 克隆一个仓库、快速启动一个项目
# 克隆示例项目的仓库 git clone https://github.com/electron/electron-quick-start # 进入这个仓库 cd electron-quick-start # 安装依赖并运行 npm install && npm start
3. 手动搭建一个 electron 项目
electrondemo01
electrondemo01
目录下面新建三个文件: index.html
、main.js
、package.json
index.html
里面用 css
进行布局(之前怎么写如今仍是怎么写)main.js
中写以下代码var electron =require('electron'); //electron 对象的引用 const app=electron.app; //BrowserWindow 类的引用 const BrowserWindow=electron.BrowserWindow; let mainWindow=null; //监听应用准备完成的事件 app.on('ready',function(){ //监听应用准备完成的事件 app.on('ready',function(){ //建立窗口 mainWindow=new BrowserWindow({width: 800, height: 600}); mainWindow.loadFile('index.html'); mainWindow.on('closed', function () { mainWindow = null; }) }) }) //监听全部窗口关闭的事件 app.on('window-all-closed', function () { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit(); } })
electron . #注意:命令后面有个点
4. electron-forge 搭建一个 electron 项目
electron-forge
至关于electron
的一个脚手架,可让咱们更方便的建立、运行、打包electron
项目
npm install -g electron-forge electron-forge init my-new-app cd my-new-app npm start
Electron
运行 package.json
的 main
脚本的进程被称为主进程。web
页面来展现用户界面。 一个 Electron
应用老是有且只有一个主进程。Electron
使用了 Chromium
(谷歌浏览器)来展现 web
页面,因此 Chromium
的 多进程架构也被使用到。 每一个 Electron
中的 web
页面运行在它本身的渲染进程中。BrowserWindow
实例建立页面。每一个 BrowserWindow
实例都在本身的渲 染进程里运行页面。 当一个 BrowserWindow
实例被销毁后,相应的渲染进程也会被终止thread
)。更准确的定义是: 线程是“一个进程内部的控制序列”。在普通的浏览器中,
web
页面一般在一个沙盒环境中运行,不被容许去接触原生的资源。 然而Electron
的用户在Node.js
的API
支持下能够在页面中和操做系统进行一些底层交 互。
Nodejs
在主进程和渲染进程中均可以使用。渲染进程由于安全限制,不能直接操做生GUI
。虽然如此,由于集成了 Nodejs,渲染进程也有了操做系统底层API
的能力,Nodejs
中经常使用的Path
、fs
、Crypto
等模块在Electron
能够直接使用,方便咱们处理连接、路径、 文件MD5
等,同时npm
还有成千上万的模块供咱们选择。
var fs = require('fs'); var content = document.getElementById('content'); var button = document.getElementById('button'); button.addEventListener('click',function(e){ fs.readFile('package.json','utf8',function(err,data){ content.textContent = data; console.log(data); }); });
mainWindow.webContents.openDevTools();
Electron
模块介绍、remote
模块、通 过BrowserWindow
打开新窗口
remote
模块提供了一种在渲染进程(网页)和主进程之间进行进程间通信(IPC
)的简便途径
Electron
中, 与GUI
相关的模块(如dialog
,menu
等)只存在于主进程,而不在渲染进程中 。为了能从渲染进程中使用它们,须要用ipc
模块来给主进程发送进程间消息。使用remote
模块,能够调用主进程对象的方法,而无需显式地发送进程间消息,这相似于Java
的RMI
Electron
渲染进程中经过remote
模块调用主进程中的BrowserWindow
打开新窗口
// 主进程代码
const electron = require('electron'); // 控制应用生命周期的模块 const {app} = electron; // 建立本地浏览器窗口的模块 const {BrowserWindow} = electron; // 指向窗口对象的一个全局引用,若是没有这个引用,那么当该 javascript 对象被垃圾回收 的 // 时候该窗口将会自动关闭 let win; function createWindow() { // 建立一个新的浏览器窗口 win = new BrowserWindow({width: 1104, height: 620});//570+50 // 而且装载应用的 index.html 页面 win.loadURL(`file://${__dirname}/html/index.html`); // 打开开发工具页面 win.webContents.openDevTools(); //当窗口关闭时调用的方法 win.on('closed', () => { // 解除窗口对象的引用,一般而言若是应用支持多个窗口的话,你会在一个数组里 // 存放窗口对象,在窗口关闭的时候应当删除相应的元素。 win = null; }); } // 当 Electron 完成初始化而且已经建立了浏览器窗口,则该方法将会被调用。 // 有些 API 只能在该事件发生后才能被使用 app.on('ready', createWindow); // 当全部的窗口被关闭后退出应用 app.on('window-all-closed', () => { // 对于 OS X 系统,应用和相应的菜单栏会一直激活直到用户经过 Cmd + Q 显式退出 if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { // 对于 OS X 系统,当 dock 图标被点击后会从新建立一个 app 窗口,而且不会有其余 // 窗口打开 if (win === null) { createWindow(); } }); // 在这个文件后面你能够直接包含你应用特定的由主进程运行的代码。 // 也能够把这些代码放在另外一个文件中而后在这里导入 // 渲染进程代码 /src/render/index.js // 打开新窗口属性用法有点相似vscode打开新的窗口 const btn = document.querySelector('#btn'); const path = require('path'); const BrowerWindow = require('electron').remote.BrowserWindow; btn.onclick = () => { win = new BrowerWindow({ width: 300, height: 200, frame: false, // false隐藏关闭按钮、菜单选项 true显示 fullscreen:true, // 全屏展现 transparent: true }) win.loadURL(path.join('file:',__dirname,'news.html')); win.on('close',()=>{win = null}); }
Electron
中Menu
模块能够用来建立原生菜单,它可用做应用菜单和context
菜单
这个模块是一个主进程的模块,而且能够经过
remote
模块给渲染进程调用
// main/menu.js const { Menu } = require('electron') // 文档 https://electronjs.org/docs/api/menu-item // 菜单项目 let menus = [ { label: '文件', submenu: [ { label: '新建文件', accelerator: 'ctrl+n', // 绑定快捷键 click: function () { // 绑定事件 console.log('新建文件') } }, { label: '新建窗口', click: function () { console.log('新建窗口') } } ] }, { label: '编辑', submenu: [ { label: '复制', role: 'copy' // 调用内置角色实现对应功能 }, { label: '剪切', role: 'cut' // 调用内置角色实现对应功能 } ] }, { label: '视图', submenu: [ { label: '浏览' }, { label: '搜索' } ] } ] let m = Menu.buildFromTemplate(menus) Menu.setApplicationMenu(m) // 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/menu.js') };
咱们给菜单绑定事件,在命令行控制台能够看到
不推荐使用这种方式,建议在主进程中使用
1. remote
经过
remote
调用主进程的方法
// 菜单引入的方式发生变化 const { Menu } = require('electron').remote // 其余代码和上面菜单同样 // ...
2. 加入index.html
<script src="render/menu.js"></script>
1. 定义菜单
// render/menu.js // 在渲染进程中经过remote模块调用主进程中的模块 const { Menu } = require('electron').remote const { remote } = require('electron') // 文档 https://electronjs.org/docs/api/menu-item // 菜单项目 let menus = [ { label: '文件', submenu: [ { label: '新建文件', accelerator: 'ctrl+n', // 绑定快捷键 click: function () { // 绑定事件 console.log('新建文件') } }, { label: '新建窗口', click: function () { console.log('新建窗口') } } ] }, { label: '编辑', submenu: [ { label: '复制', role: 'copy' // 调用内置角色实现对应功能 }, { label: '剪切', role: 'cut' // 调用内置角色实现对应功能 } ] }, { label: '视图', submenu: [ { label: '浏览' }, { label: '搜索' } ] } ] let m = Menu.buildFromTemplate(menus) // Menu.setApplicationMenu(m) // 绑定右键菜单 window.addEventListener('contextmenu', (e)=>{ e.preventDefault() m.popup({ window: remote.getCurrentWindow() }) }, false)
2. 引入
<!--index.html--> <script src="render/menu.js"></script>
有时候咱们想在渲染进程中经过一个事件去执行主进程里面的方法。或者在渲染进程中通知 主进程处理事件,主进程处理完成后广播一个事件让渲染进程去处理一些事情。这个时候就 用到了主进程和渲染进程之间的相互通讯
Electron
主进程,和渲染进程的通讯主要用到两个模块:ipcMain
和ipcRenderer
ipcMain
:当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息,固然也有可能从主进程向渲染进程发送消息。ipcRenderer
: 使用它提供的一些方法从渲染进程 (web
页面) 发送同步或异步的消息到主进程。 也能够接收主进程回复的消息间接实现渲染进程执行主进程里面的方法
1. 引入ipcRender
<!--src/index.html--> <button id="send">在 渲染进程中执行主进程里的方法(异步)</button> <script src="render/ipcRender.js"></script>
2. 引入ipcMain
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain.js') };
3. 渲染进程发送消息
// src/render/ipcRender.js //渲染进程 let send = document.querySelector('#send'); const { ipcRenderer } = require('electron'); send.onclick = function () { // 传递消息给主进程 // 异步 ipcRenderer.send('sendMsg', {name:'poetries', age: 23}) }
2. 主进程接收消息
// src/main/ipcMain.js //主进程 const { ipcMain } = require('electron') // 主进程处理渲染进程广播数据 ipcMain.on('sendMsg', (event, data)=> { console.log('data\n ', data) console.log('event\n ', event) })
渲染进程给主进程发送异步消息,主进程接收到异步消息之后通知渲染进程
1. 引入ipcRender
<!--src/index.html--> <button id="sendFeedback">在 渲染进程中执行主进程里的方法,并反馈给主进程(异步)</button> <script src="render/ipcRender.js"></script>
2. 引入ipcMain
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain.js') };
3. 渲染进程发送消息
// src/render/ipcRender.js //渲染进程 let sendFeedback = document.querySelector('#sendFeedback'); const { ipcRenderer } = require('electron'); // 向主进程发送消息 sendFeedback.onclick = function () { // 触发主进程里面的方法 ipcRenderer.send('sendFeedback', {name:'poetries', age: 23}) }
4. 主进程收到消息处理并广播反馈通知渲染进程
// src/main/ipcMain.js //主进程 const { ipcMain } = require('electron') // 主进程处理渲染进程广播数据,并反馈给渲染进程 ipcMain.on('sendFeedback', (event, data)=> { // console.log('data\n ', data) // console.log('event\n ', event) // 主进程给渲染进程广播数据 event.sender.send('sendFeedbackToRender', '来自主进程的反馈') })
5. 渲染进程处理主进程广播的数据
// src/render/ipcRender.js // 向主进程发送消息后,接收主进程广播的事件 ipcRenderer.on('sendFeedbackToRender', (e, data)=>{ console.log('event\n ', e) console.log('data\n ', data) })
1. 引入ipcRender
<!--src/index.html--> <button id="sendSync">渲染进程和主进程同步通讯</button> <script src="render/ipcRender.js"></script>
2. 引入ipcMain
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain.js') };
3. 渲染进程给主进程同步通讯
// src/render/ipcMain.js let sendSync = document.querySelector('#sendSync'); // 渲染进程和主进程同步通讯 sendSync.onclick = function () { // 同步广播数据 let msg = ipcRenderer.sendSync('sendsync', {name:'poetries', age: 23}) // 同步返回主进程反馈的数据 console.log('msg\n ', msg) }
4. 主进程接收数据处理
// src/main/ipcMain.js // 渲染进程和主进程同步通讯 接收同步广播 ipcMain.on('sendsync', (event, data)=> { // console.log('data\n ', data) // console.log('event\n ', event) // 主进程给渲染进程广播数据 event.returnValue ='渲染进程和主进程同步通讯 接收同步广播,来自主进程的反馈.'; })
通常都是在渲染进程中执行广播操做,去通知主进程完成任务
1. 引入openWindow
<!--src/index.html--> <button id="sendSync">渲染进程和主进程同步通讯</button> <script src="render/openWindow.js"></script>
2. 引入ipcMain2
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain2.js') };
3. 渲染进程通知主进程打开窗口
// src/render/openWindow.js /* eslint-disable */ let openWindow = document.querySelector('#openWindow'); var { ipcRenderer } = require('electron'); // 渲染进程和渲染进程直接的通讯======== openWindow.onclick = function () { // 经过广播的形式 通知主进程执行操做 ipcRenderer.send('openwindow', {name:'poetries', age: 23}) }
4. 主进程收到通知执行操做
// src/main/ipcMain2.js /* eslint-disable */ let { ipcMain,BrowserWindow } = require('electron') const path = require('path') let win; // 接收到广播 ipcMain.on('openwindow', (e, data)=> { // 调用window打开新窗口 win = new BrowserWindow({ width: 400, height: 300, }); win.loadURL(path.join('file:',__dirname, '../news.html')); win.webContents.openDevTools() win.on('closed', () => { win = null; }); })
也就是两个窗口直接的通讯
Electron
渲染进程经过localstorage
给另外一个渲染进程传值
1. 引入openWindow
<!--src/index.html--> <button id="sendSync">渲染进程和主进程同步通讯</button> <script src="render/openWindow.js"></script>
2. 引入ipcMain2
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain2.js') };
3. 渲染进程通知主进程打开窗口
// src/render/openWindow.js /* eslint-disable */ let openWindow = document.querySelector('#openWindow'); var { ipcRenderer } = require('electron'); // 渲染进程和渲染进程直接的通讯======== openWindow.onclick = function () { // 经过广播的形式 通知主进程执行操做 ipcRenderer.send('openwindow', {name:'poetries', age: 23}) // ======= localstorage传值 ===== localStorage.setItem('username', 'poetries') }
4. 新建news页面
<!--src/news.html--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> news page </body> <script src="render/news.js"></script> </html> // src/render/news.js let username = localStorage.getItem('username') console.log(username)
5. 主进程收到通知执行操做
// src/main/ipcMain2.js /* eslint-disable */ let { ipcMain,BrowserWindow } = require('electron') const path = require('path') let win; // 接收到广播 ipcMain.on('openwindow', (e, data)=> { // 调用window打开新窗口 win = new BrowserWindow({ width: 400, height: 300, }); win.loadURL(path.join('file:',__dirname, '../news.html')); win.webContents.openDevTools() win.on('closed', () => { win = null; }); })
经过
BrowserWindow
和webContents
模块实现渲染进程和渲染进程的通讯
webContents
是一个事件发出者.它负责渲染并控制网页,也是BrowserWindow
对象的属性
须要了解的几个知识点
id
const winId = BrowserWindow.getFocusedWindow().id;
win.webContents.on('did-finish-load',(event) => {
})
win.webContents.on('did-finish-load',(event) => { win.webContents.send('msg',winId,'我是 index.html 的数据'); })
id
查找窗口let win = BrowserWindow.fromId(winId);
下面是具体演示
1. 引入openWindow
<!--src/index.html--> <button id="sendSync">渲染进程和主进程同步通讯</button> <script src="render/openWindow.js"></script>
2. 引入ipcMain2
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/ipcMain2.js') };
3. 渲染进程通知主进程打开窗口
// src/render/openWindow.js /* eslint-disable */ let openWindow = document.querySelector('#openWindow'); var { ipcRenderer } = require('electron'); // 渲染进程和渲染进程直接的通讯======== openWindow.onclick = function () { // 经过广播的形式 通知主进程执行操做 ipcRenderer.send('openwindow', {name:'poetries', age: 23}) }
4. 主进程收到通知执行操做
// src/main/ipcMain2.js let { ipcMain,BrowserWindow } = require('electron') const path = require('path') let win; // 接收到广播 ipcMain.on('openwindow', (e, userInfo)=> { // 调用window打开新窗口 win = new BrowserWindow({ width: 400, height: 300, }); win.loadURL(path.join('file:',__dirname, '../news.html')); // 新开窗口调试模式 win.webContents.openDevTools() // 把渲染进程传递过来的数据再次传递给渲染进程news // 等待窗口加载完 win.webContents.on('did-finish-load', ()=>[ win.webContents.send('toNews', userInfo) ]) win.on('closed', () => { win = null; }); })
5. news接收主进程传递的数据
数据通过渲染进程->主进程->
news
渲染进程
<!--news页面--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> news page </body> <script src="render/news.js"></script> </html> // src/render/news.js var { ipcRenderer } = require('electron'); // let username = localStorage.getItem('username') // console.log(username) // 监听主进程传递过来的数据 ipcRenderer.on('toNews',(e, userInfo)=>{ console.log(userInfo) })
那么,这里有一个问题,
news
进程接收到了广播后如何给出反馈呢?
1. 在主进程中获取窗口ID传递
// src/main/ipcMain2.js let { ipcMain,BrowserWindow } = require('electron') const path = require('path') let win; // 接收到广播 ipcMain.on('openwindow', (e, userInfo)=> { // 获取当前窗口ID 放在第一行保险 由于后面也打开了新窗口使得获取的ID有问题 let winId = BrowserWindow.getFocusedWindow().id // 调用window打开新窗口 win = new BrowserWindow({ width: 400, height: 300, }); win.loadURL(path.join('file:',__dirname, '../news.html')); // 新开窗口调试模式 win.webContents.openDevTools() // 把渲染进程传递过来的数据再次传递给渲染进程news // 等待窗口加载完 win.webContents.on('did-finish-load', ()=>[ win.webContents.send('toNews', userInfo, winId) ]) win.on('closed', () => { win = null; }); })
2. 在news进程中广播数据
// src/render/news.js var { ipcRenderer } = require('electron'); // 注意这里 在渲染进程中须要从remote中获取BrowserWindow const BrowerWindow = require('electron').remote.BrowserWindow; // let username = localStorage.getItem('username') // console.log(username) // 监听主进程传递过来的数据 ipcRenderer.on('toNews',(e, userInfo, winId)=>{ // windID 第一个窗口ID // 获取对应ID的窗口 let firstWin = BrowerWindow.fromId(winId) firstWin.webContents.send('toIndex', '来自news进程反馈的信息') console.log(userInfo) })
3. 在另外一个渲染进程中处理广播
/* eslint-disable */ let openWindow = document.querySelector('#openWindow'); var { ipcRenderer } = require('electron'); // 渲染进程和渲染进程直接的通讯======== openWindow.onclick = function () { // 传递消息给主进程 ipcRenderer.send('openwindow', {name:'poetries', age: 23}) // 传递给打开的窗口 渲染进程和渲染进程直接的通讯 localStorage.setItem('username', 'poetries') } // 接收news渲染进程传递回来的消息 ipcRenderer.on('toIndex', (e, data)=>{ console.log('===', data) })
Electron Shell
模块在用户默认浏览器 中打开URL
以及Electron DOM webview
标签。Shell
既属于主进程模块又是渲染进程模块
shell
模块提供了集成其余桌面客户端的关联功能
1. 引入
<!--index.html--> <button id="shellDom">经过shell打开外部连接</button> <script src="render/shell.js"></script>
2. shell.js
// src/render/shell.js const { shell } = require('electron') let shellDom = document.querySelector('#shellDom'); shellDom.onclick = function (e) { shell.openExternal('https://github.com/poetries') }
Electron DOM
<webview>
标签
Webview
与iframe
有点类似,可是与iframe
不一样,webview
和你的应用运行的是不一样的进程。它不拥有渲染进程的权限,而且应用和嵌入内容之间的交互所有都是异步的。由于这能 保证应用的安全性不受嵌入内容的影响。
<!--src/index.html中引入--> <webview id="webview" src="http://blog.poetries.top" style="position:fixed; width:100%; height:100%"> </webview>
shell
模块<webview>
结合Menu
模块使用案例1. 新建src/render/webview.js
/* eslint-disable */ var { ipcRenderer } = require('electron'); let myWebview = document.querySelector('#myWebview') ipcRenderer.on('openwebview', (e, url)=>{ myWebview.src = url })
2. 引入src/index.html
<webview id="myWebview" src="http://blog.poetries.top" style="position:fixed; width:100%; height:100%"> </webview> <script src="render/webview.js"></script>
3. 新建src/main/menu.js
/* eslint-disable */ const { shell, Menu, BrowserWindow } = require('electron'); // 当前窗口渲染网页 function openWebView(url) { // 获取当前窗口Id let win = BrowserWindow.getFocusedWindow() // 广播通知渲染进程打开webview win.webContents.send('openwebview', url) } // 在窗口外打开网页 function openWeb(url) { shell.openExternal(url) } let template = [ { label: '帮助', submenu: [ { label: '关于咱们', click: function () { openWeb('http://blog.poetries.top') } }, { type: 'separator' }, { label: '联系咱们', click: function () { openWeb('https://github.com/poetries') } } ] }, { label: '加载网页', submenu: [ { label: '博客', click: function () { openWebView('http://blog.poetries.top') } }, { type: 'separator' // 分隔符 }, { label: 'GitHub', click: function () { openWebView('https://github.com/poetries') } }, { type: 'separator' // 分隔符 }, { label: '简书', click: function () { openWebView('https://www.jianshu.com/users/94077fcddfc0/timeline') } } ] }, { label: '视频网站', submenu: [ { label: '优酷', click: function () { openWebView('https://www.youku.com') } }, { type: 'separator' // 分隔符 }, { label: '爱奇艺', click: function () { openWebView('https://www.iqiyi.com/') } }, { type: 'separator' // 分隔符 }, { label: '腾讯视频', click: function () { openWebView('https://v.qq.com/') } } ] } ] let m = Menu.buildFromTemplate(template) Menu.setApplicationMenu(m)
4. 引入menu
// 在主进程src/index.js中引入 const createWindow = () => { // 建立菜单 // 引入菜单模块 require('./main/menu.js') };
dialog
属于主进程中的模块
dialog
模块提供了api
来展现原生的系统对话框,例如打开文件框,alert
框, 因此web
应用能够给用户带来跟系统应用相同的体验
1. 在src/index.html中引入
<button id="showError">showError</button><br /> <button id="showMsg">showMsg</button><br /> <button id="showOpenDialog">showOpenDialog</button><br /> <button id="saveDialog">saveDialog</button><br /> <script src="render/dialog.js"></script>
2. 新建render/dialog.js
// render/dialog.js let showError = document.querySelector('#showError'); let showMsg = document.querySelector('#showMsg'); let showOpenDialog = document.querySelector('#showOpenDialog'); let saveDialog = document.querySelector('#saveDialog'); var {remote} = require('electron') showError.onclick = function () { remote.dialog.showErrorBox('警告', '操做有误') } showMsg.onclick = function () { remote.dialog.showMessageBox({ type: 'info', title: '提示信息', message: '内容', buttons: ['肯定', '取消'] },function(index){ console.log(index) }) } showOpenDialog.onclick = function () { remote.dialog.showOpenDialog({ // 打开文件夹 properties: ['openDirectory', 'openFile'] // 打开文件 // properties: ['openFile'] }, function (data) { console.log(data) }) } saveDialog.onclick = function () { remote.dialog.showSaveDialog({ title: 'Save File', defaultPath: '/Users/poetry/Downloads/', // filters 指定一个文件类型数组,用于规定用户可见或可选的特定类型范围 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) { // 不是真的保存 ,具体还需nodejs处理 console.log(path) }) }
showError
remote.dialog.showErrorBox('警告', '操做有误')
showMessageBox
remote.dialog.showMessageBox({ type: 'info', title: '提示信息', message: '内容', buttons: ['肯定', '取消'] },function(index){ console.log(index) })
showOpenDialog
remote.dialog.showOpenDialog({ // 打开文件夹 properties: ['openDirectory', 'openFile'] // 打开文件 // properties: ['openFile'] }, function (data) { console.log(data) })
showSaveDialog
remote.dialog.showSaveDialog({ title: 'Save File', defaultPath: '/Users/poetry/Downloads/', // filters 指定一个文件类型数组,用于规定用户可见或可选的特定类型范围 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) { // 不是真的保存 ,具体还需nodejs处理 console.log(path) })
代码 https://github.com/poetries/electron-demo/tree/master/notepad
系统托盘,托盘右键菜单、托盘图标闪烁 点击右上角关闭按钮隐藏到托盘(仿杀毒软件)
1. 引入文件
// src/index.js const createWindow = () => { require('./main/tray.js') };
2. Electron 建立任务栏图标以及任务栏图标右键菜单
// src/main/tray.js var { Menu, Tray, app, BrowserWindow } = require('electron'); const path = require('path'); var appIcon = new Tray(path.join(__dirname, '../static/lover.png')); const menu = Menu.buildFromTemplate([ { label: '设置', click: function() {} //打开相应页面 }, { label: '帮助', click: function() {} }, { label: '关于', click: function() {} }, { label: '退出', click: function() { // BrowserWindow.getFocusedWindow().webContents().send('close-main-window'); app.quit(); } }]) // 鼠标放上去提示信息 appIcon.setToolTip('hello poetries'); appIcon.setContextMenu(menu);
3. 监放任务栏图标的单击、双击事件
// 实现点击关闭按钮,让应用保存在托盘里面,双击托盘打开 let win = BrowserWindow.getFocusedWindow() win.on('close', (e)=>{ e.preventDefault() win.hide() }) iconTray.on('double-click', (e)=>{ win.show() })
4. Electron 点击右上角关闭按钮隐藏任务栏图标
const win = BrowserWindow.getFocusedWindow(); win.on('close', (e) =>{ console.log(win.isFocused()); if (!win.isFocused()) { win = null; } else { e.preventDefault();/*阻止应用退出*/ win.hide();/*隐藏当前窗口*/ } })
5. Electron 实现任务栏闪烁图标
var appIcon = new Tray(path.join(__dirname, '../static/lover.png')); timer = setInterval(function() { count++; if (count % 2 == 0) { appIcon.setImage(path.join(__dirname, '../static/empty.ico')) } else { appIcon.setImage(path.join(__dirname, '../static/lover.png')) } }, 500);
1. Electron 实现消息通知
Electron
里面的消息通知是基于h5
的通知api
实现的
文档 https://developer.mozilla.org/zh-CN/docs/Web/API/notification
1. 新建notification.js
// h5api实现通知 const path = require('path') let options = { title: 'electron 通知API', body: 'hello poetries', icon: path.join('../static/img/favicon2.ico') // 通知图标 } document.querySelector('#showNotification').onclick = function () { let myNotification = new window.Notification(options.title, options) // 消息可点击 myNotification.onclick = function () { console.log('click notification') } }
2. 引入
<!--src/index.html--> <button id="showNotification">弹出消息通知</button> <script src="render/notification.js"></script>
mac
上的消息通知
1. 基本使用
// 监听网络变化 // 端开网络 再次链接测试 window.addEventListener('online', function(){ console.log('online') }); window.addEventListener('offline', function(){ console.log('offline') });
2. 监听网络变化实现消息通知
// 端开网络 再次链接测试 // 监听网络变化实现消息通知 window.addEventListener('online', function(){ console.log('online') }); window.addEventListener('offline', function(){ // 断开网络触发事件 var options = { title: 'QQ邮箱', body: '网络异常,请检查你的网络', icon: path.join('../static/img/favicon2.ico') // 通知图标 } var myNotification = new window.Notification(options.title, options) myNotification.onclick = function () { console.log('click notification') } });
Electron
注册全局快捷键 (globalShortcut
) 以及clipboard
剪 切板事件以及nativeImage
模块(实现相似播放器点击机器码自动复制功 能)
1. 新建src/main/shortCut.js
const {globalShortcut, app} = require('electron') app.on('ready', ()=>{ // 注册全局快捷键 globalShortcut.register('command+e', ()=>{ console.log(1) }) // 检测快捷键是否注册成功 true是注册成功 let isRegister = globalShortcut.isRegistered('command+e') console.log(isRegister) }) // 退出的时候取消全局快捷键 app.on('will-quit', ()=>{ globalShortcut.unregister('command+e') })
2. 引入src/index.js
// 注意在外部引入便可 不用放到app中 require('./main/shortCut.js')
1. html
<!--src/index.html--> <div> <h2>双击下面信息复制</h2> <p id='msg'>123456789</p> <button id="plat">粘贴</button><br /> <input id="text" type="text"/> </div>. <div> <h2>复制图片到界面</h2> <button id="copyImg">复制图片</button><br /> </div> <script src="render/clipboard.js"></script>
2. 新建src/render/clipboard.js
// clipboard能够在主进程或渲染进程使用 const { clipboard, nativeImage } = require('electron') //复制 // 运行ctrl+v可看到复制的内容 // clipboard.writeText('poetries') // clipboard.readText() //获取复制的内容 粘贴 // 双击复制消息 let msg = document.querySelector('#msg') let plat = document.querySelector('#plat') let text = document.querySelector('#text') msg.ondblclick = function () { clipboard.writeText(msg.innerHTML) alert(msg.innerHTML) } plat.onclick = function () { text.value = clipboard.readText() } // 复制图片显示到界面 let copyImg = document.querySelector('#copyImg') copyImg.onclick = function () { // 结合nativeImage模块 let image = nativeImage.createFromPath('../static/img/lover.png') // 复制图片 clipboard.writeImage(image) // 粘贴图片 let imgSrc = clipboard.readImage().toDataURL() // base64图片 // 显示到页面上 let imgDom = new Image() imgDom.src = imgSrc document.body.appendChild(imgDom) }
1. electron-vue 的一些资源
Electron-vue
文档 https://simulatedgreg.gitbooks.io/electron-vue/content/cn
2. electron-vue 环境搭建、建立项目
npm install -g vue-cli vue init simulatedgreg/electron-vue my-project cd my-project yarn # or npm install yarn run dev # or npm run dev
3. electron-vue 目录结构分析
1. electron-vue UI 框架 ElementUi 的使用
2. electron-vue 中使用 sass
# 安装 sass-loader: npm install --save-dev sass-loader node-sass <!--vue 文件中修改 style 为以下代码:--> <style lang="scss"> body { /* SCSS */ } </style>
electron-vue 中隐藏顶部菜单隐藏顶部最大化、最小化、关闭按钮 自定最大化、最小化 、关闭按钮
1. electron-vue 中隐藏顶部菜单
// src/main/index.js mainWindow.setMenu(null)
2. electron-vue 中隐藏关闭 最大化 最小化按钮
// src/main/index.js mainWindow = new BrowserWindow({ height: 620, useContentSize: true, width: 1280, frame: false /*去掉顶部导航 去掉关闭按钮 最大化最小化按钮*/ })
3 .electron-vue 自定义关闭/最大化最小化按钮
// 注意在mac下不须要监听窗口最大最小化、觉得系统默认支持,这个只是针对windows平台 ipc.on('window-min',function() { mainWindow.minimize(); }) //登陆窗口最大化 ipc.on('window-max',function(){ if (mainWindow.isMaximized()) { mainWindow.restore(); } else { mainWindow.maximize(); } }) ipc.on('window-close',function() { mainWindow.close(); })
4. electron-vue 自定义导航可拖拽
css
: -webkit-app-region: drag;
css
: -webkit-app-region: no-drag;
1. 项目搭建
npm install -g vue-cli vue init simulatedgreg/electron-vue my-project cd my-project yarn # or npm install yarn run dev # or npm run dev
2. 安装一些依赖
# 安装 sass-loader: npm install --save-dev sass-loader node-sass # 安装elementUI、js-md5 npm i element-ui js-md5 -S 在.electron-vue/webpack.renderer.config.js中配置sass-loader就能够编写``sass`了 <!--vue 文件中修改 style 为以下代码:--> <style lang="scss"> body { /* SCSS */ } </style>
1. src/main/index.js
function createWindow () { // 去掉顶部菜单 mainWindow.setMenu(null) // 菜单项 require('./model/menu.js'); // 系统托盘相关 require('./model/tray.js');
2. src/main/menu.js
菜单配置
const { Menu,ipcMain,BrowserWindow} = require('electron'); //右键菜单 const contextMenuTemplate=[ { label: '复制', role: 'copy' }, { label: '黏贴', role: 'paste' }, { type: 'separator' }, //分隔线 { label: '其余功能', click: () => { console.log('click') } } ]; const contextMenu=Menu.buildFromTemplate(contextMenuTemplate); ipcMain.on('contextmenu',function(){ contextMenu.popup(BrowserWindow.getFocusedWindow()); })
3. src/main/tray.js
系统托盘配置
托盘点击监听事件只有在
windows
下才生效,mac
系统默认支持
(function () { const path=require('path'); const {app,Menu,BrowserWindow,Tray, shell} = require('electron'); //建立系统托盘 const tray = new Tray(path.resolve(__static, 'favicon.png')) //给托盘增长右键菜单 const template= [ { label: '设置', click: function () { shell.openExternal('http://blog.poetries.top') } }, { label: '帮助', click: function () { shell.openExternal('http://blog.poetries.top/2019/01/06/electron-summary') } }, { label: '关于', click: function () { shell.openExternal('https://github.com/poetries/yuqing-monitor-electron') } }, { label: '退出', click: function () { // BrowserWindow.getFocusedWindow().webContents().send('close-main-window'); app.quit(); } } ]; const menu = Menu.buildFromTemplate(template); tray.setContextMenu(menu); tray.setToolTip('舆情监控系统'); //监听关闭事件隐藏到系统托盘 // 这里须要注意:在window中才生效,mac下系统默认支持 // var win = BrowserWindow.getFocusedWindow(); // win.on('close',(e)=>{ // if(!win.isFocused()){ // win=null; // }else{ // e.preventDefault(); /*阻止应用退出*/ // win.hide(); /*隐藏当前窗口*/ // } // }) // //监听托盘的双击事件 // tray.on('double-click',()=>{ // win.show(); // }) })()
4. src/main/shortCut.js
快捷键配置
在src/main/index.js
中引入(require('src/main/shortCut.js')
)便可,不须要放到app
监控中
var {globalShortcut, app} = require('electron') app.on('ready', ()=>{ // 注册全局快捷键 globalShortcut.register('command+e', ()=>{ console.log(1) }) // 检测快捷键是否注册成功 true是注册成功 let isRegister = globalShortcut.isRegistered('command+e') console.log(isRegister) }) // 退出的时候取消全局快捷键 app.on('will-quit', ()=>{ globalShortcut.unregister('command+e') })
1. src/render/main.js配置
import Vue from 'vue' import axios from 'axios' import App from './App' import router from './router' import store from './store' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import VueHighcharts from 'vue-highcharts'; import VueSocketIO from 'vue-socket.io' Vue.use(ElementUI); Vue.use(VueHighcharts); //引入socket.io配置链接 Vue.use(new VueSocketIO({ debug: true, connection: 'http://118.123.14.36:3000', vuex: { store, actionPrefix: 'SOCKET_', mutationPrefix: 'SOCKET_' } })) if (!process.env.IS_WEB) Vue.use(require('vue-electron')) Vue.http = Vue.prototype.$http = axios Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ components: { App }, router, store, template: '<App/>' }).$mount('#app')
2. 路由配置src/renderer/router/index.js
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ { path: '/home', name: 'home', component: require('@/components/Home').default }, { path: '/report', name: 'report', component: require('@/components/Report').default }, { path: '/negativereport', name: 'negativereport', component: require('@/components/NegativeReport').default }, { path: '/positivereport', name: 'positivereport', component: require('@/components/PositiveReport').default }, { path: '/keyword', name: 'keyword', component: require('@/components/KeyWord').default }, { path: '/alarm', name: 'alarm', component: require('@/components/Alarm').default }, { path: '/msg', name: 'msg', component: require('@/components/Msg').default }, { path: '*', redirect: '/home' } ] })
3. 在渲染进程中使用主进程方式
// electron挂载到了vue实例上 $electron this.$electron.shell
须要注意的是打包
mac
版本在mac
系统上打包,打包window
则在windows
上打包,能够避免不少问题
# 在不一样平台上执行便可打包应用
npm run build
1. electron 中构建应用最经常使用的模块
electron-packager
electron-builder
electron-packager
和electron-builder
在本身单首创建的应用用也能够完成打包功 能。可是因为配置太复杂因此咱们不建议单独配置
2. electron-forge
electron-forge package electron-forge make
3. electron-vue中的打包方式
# https://simulatedgreg.gitbooks.io/electron-vue/content/cn/using-electron-packager. html # 之须要执行一条命令 npm run build
1. 修改package.json
2. 修改src/index.ejs标题信息
3. 修改build/icons图标
1. 建立应用托盘的时候可能会遇到错误
static
里面,而后注意下面写法。var tray = new Tray(path.join(__static,'favicon.ico'))
.png
格式重试2. 模块问题可能会遇到的错误
解决办法
node_modules
而后从新用 npm install
安装依赖yarn
来安装模块最后执行
yarn run build
便可
项目截图
舆情监控系统页面
系统系统托盘、
electron
消息通知 (相似腾讯新闻)
做者:poetries连接:https://www.jianshu.com/p/2244653515a7来源:简书著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。