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
开发的
1. Electron 是由谁开发的?
Electron
是由
Github
开发
2. Electron 是什么?
Electron
是一个用
HTML
,
CSS
和
JavaScript
来构建跨平台桌面应用程序的一个开源库
3. Electron 把 HTML,CSS 和 JavaScript 组合的程序构建为跨平台桌面应用程序的原理 是什么?
原理为
Electron
通过将
Chromium
和
Node.js
合并到同一个运行时环境中,并将其打包为
Mac
,
Windows
和
Linux
系统下的应用来实现这一目的。
4. Electron 何时出现的,为什么会出现?
Electron
于
2013
年作为构建
Atom
的框架而被开发出来。这两个项目在
2014
春季开源。 (Atom:为 Github 上可编程的文本编辑器)
一些历史:
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
的版本会对其进行更新,具 体时间根据升级所需的工作量而定。
Electron
的核心理念是:保持
Electron
的体积小和可持续性开发。
如:为了保持
Electron
的小巧 (文件体积) 和可持续性开发 (以防依赖库和
API
的泛滥) ,
Electron
限制了所使用的核心项目的数量。
比如
Electron
只用了
Chromium
的渲染库而不是其全部组件。这使得升级
Chromium
更加容易,但也意味着
Electron
缺少了
Google Chrome
里的一些浏览器相关的特性。 添加到
Electron
的新功能应该主要是原生
API
。 如果可以的话,一个功能应该尽可能的成 为一个
Node.js
模块。
新建一个项目目录 例如:
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
运行 package.json
的 main
脚本的进程被称为主进程。
在主进程中运行的脚本通过创建 web
页面来展示用户界面。 一个 Electron
应用总是有且只有一个主进程。
由于 Electron
使用了 Chromium
(谷歌浏览器)来展示 web
页面,所以 Chromium
的 多进程架构也被使用到。 每个 Electron
中的 web
页面运行在它自己的渲染进程中。
主进程使用 BrowserWindow
实例创建页面。每个 BrowserWindow
实例都在自己的渲 染进程里运行页面。 当一个 BrowserWindow
实例被销毁后,相应的渲染进程也会被终止
进程:进程是计算机中的程序关于某数据集合上的一次运行活动,是 系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程:在一个程序里的一个执行路线就叫做线程(thread
)。更准确的定义是: 线程是“一个进程内部的控制序列”。
线程和进程:一个程序至少有一个进程,一个进程至少有一个线程
3.3 Electron 渲染进程中通过 Nodejs 读取本地文件
在普通的浏览器中,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);
3.4 Electron 开启调试模式
mainWindow.webContents.openDevTools();
Electron
中, 与 GUI
相关的模块(如 dialog
, menu
等)只存在于主进程,而不在渲染进程中 。为了能从渲染进程中使用它们,需要用ipc
模块来给主进程发送进程间消息。使用 remote
模块,可以调用主进程对象的方法,而无需显式地发送进程间消息,这类似于 Java
的 RMI
4.3 通过BrowserWindow 打开新窗口
Electron
渲染进程中通过 remote
模块调用主进程中的 BrowserWindow
打开新窗口
https://electronjs.org/docs/api/browser-window
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});
// 在渲染进程中通过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)
渲染进程 https://electronjs.org/docs/api/ipc-renderer
主进程 https://electronjs.org/docs/api/ipc-main
6.1 主进程与渲染进程之间的通信
有时候我们想在渲染进程中通过一个事件去执行主进程里面的方法。或者在渲染进程中通知 主进程处理事件,主进程处理完成后广播一个事件让渲染进程去处理一些事情。这个时候就 用到了主进程和渲染进程之间的相互通信
Electron
主进程,和渲染进程的通信主要用到两个模块:ipcMain
和 ipcRenderer
ipcMain
:当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息,当然也有可能从主进程向渲染进程发送消息。
ipcRenderer
: 使用它提供的一些方法从渲染进程 (web
页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息
6.1.1 渲染进程给主进程发送异步消息
间接实现渲染进程执行主进程里面的方法
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)
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)
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 ='渲染进程和主进程同步通信 接收同步广播,来自主进程的反馈.';
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;
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>
<meta charset="utf-8">
<title></title>
</head>
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;
6.2.2 BrowserWindow和webContents方式实现
通过 BrowserWindow
和 webContents
模块实现渲染进程和渲染进程的通信
webContents
是一个事件发出者.它负责渲染并控制网页,也是 BrowserWindow
对象的属性
需要了解的几个知识点
获取当前窗口的 id
const winId = BrowserWindow.getFocusedWindow().id;
win.webContents.on('did-finish-load',(event) => {
win.webContents.send('msg',winId,'我是 index.html 的数据');
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
渲染进程
var { ipcRenderer } = require('electron');
// let username = localStorage.getItem('username')
// console.log(username)
// 监听主进程传递过来的数据
ipcRenderer.on('toNews',(e, userInfo)=>{
console.log(userInfo)
// 接收到广播
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)
const { shell } = require('electron')
let shellDom = document.querySelector('#shellDom');
shellDom.onclick = function (e) {
shell.openExternal('https://github.com/poetries')
7.2 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>
7.3 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
模块提供了 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('警告', '操作有误')
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)
九、实现一个类似EditPlus的简易记事本代码编辑器
代码 https://github.com/poetries/electron-demo/tree/master/notepad
十、系统托盘、托盘右键菜单、托盘图标闪烁
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);
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);
十一、消息通知、监听网络变 化、网络变化弹出通知框
11.1 消息通知
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
上的消息通知
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')
十二、注册全局快捷键/剪切板事件/nativeImage 模块
Electron
注册全局快捷键 (globalShortcut
) 以及 clipboard
剪 切板事件以及 nativeImage
模块(实现类似播放器点击机器码自动复制功 能)
12.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')
12.2 剪切板clipboard、nativeImage 模块
// 双击复制消息
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)
十三、结合electron-vue
13.1 electron-vue 的使用
1. electron-vue 的一些资源
https://github.com/SimulatedGREG/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 目录结构分析
# 安装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>
13.4.2 主进程配置
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')
13.4.3 渲染进程配置
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'
electron-packager
和 electron-builder
在自己单独创建的应用用也可以完成打包功 能。但是由于配置太复杂所以我们不建议单独配置
2. electron-forge
https://github.com/electron-userland/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
13.4.4.2 修改应用信息
1. 修改package.json