Electron启程

2019-10-21 17:17:10 浏览数 (1)

前言

Electron 可以让你使用纯 JavaScript 调用丰富的原生(操作系统) APIs 来创造桌面应用。 你可以把它看作一个 Node. js 的变体,它专注于桌面应用而不是 Web 服务器端。

这不意味着 Electron 是某个图形用户界面(GUI)库的 JavaScript 版本。 相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。

起步

代码语言:javascript复制
git clone https://github.com/electron/electron-quick-start
cd electron-quick-start
npm install
npm start

创建窗口

代码语言:javascript复制
const { app, BrowserWindow } = require('electron')

// 保持对window对象的全局引用,如果不这么做的话,当JavaScript对象被
// 垃圾回收的时候,window对象将会自动的关闭
let win

function createWindow () {
  // 创建浏览器窗口。
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // 加载index.html文件
  win.loadFile('index.html')

  // 打开开发者工具
  win.webContents.openDevTools()

  // 当 window 被关闭,这个事件会被触发。
  win.on('closed', () => {
    // 取消引用 window 对象,如果你的应用支持多窗口的话,
    // 通常会把多个 window 对象存放在一个数组里面,
    // 与此同时,你应该删除相应的元素。
    win = null
  })
}

// Electron 会在初始化后并准备
// 创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow)

// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
  // 在 macOS 上,除非用户用 Cmd   Q 确定地退出,
  // 否则绝大部分应用及其菜单栏会保持激活。
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // 在macOS上,当单击dock图标并且没有其他窗口打开时,
  // 通常在应用程序中重新创建一个窗口。
  if (win === null) {
    createWindow()
  }
})

// 在这个文件中,你可以续写应用剩下主进程代码。
// 也可以拆分成几个文件,然后用 require 导入。

主进程和渲染进程

Electron 运行 package.jsonmain.js 脚本的进程被称为主进程

在主进程中运行的脚本通过创建web页面来展示用户界面。

一个 Electron 应用总是有且只有一个主进程。

每个 Electron 中的 web 页面运行在它自己的渲染进程中。

主进程管理所有的web页面和它们对应的渲染进程。 每个渲染进程都是独立的,它只关心它所运行的 web 页面。

Electron同时在主进程和渲染进程中对Node.js 暴露了所有的接口。

主进程和渲染进程模块

两种进程都可用的模块

  • clipboard 在系统剪贴板上执行复制和粘贴操作。
  • crashReporter 将崩溃日志提交给远程服务器。
  • nativeImage 使用 PNG 或 JPG 文件创建托盘、dock和应用程序图标。
  • shell 使用默认应用程序管理文件和 url。

Main Process 模块

  • app 控制你的应用程序的事件生命周期。
  • autoUpdater 使应用程序能够自动更新
  • BrowserView 创建和控制视图,相当于Android中的Fragment。
  • BrowserWindow 创建和控制浏览器窗口,相当于Android中的Activity。
  • contentTracing 从Chromium的内容模块收集跟踪数据,以查找性能瓶颈和缓慢的操作。
  • dialog 显示用于打开和保存文件、警报等的本机系统对话框。
  • globalShortcut 在应用程序没有键盘焦点时,监听键盘事件。
  • inAppPurchase Mac App Store中的应用内购买。
  • ipcMain 从主进程到渲染进程的异步通信。
  • Menu 创建原生应用菜单和上下文菜单。
  • MenuItem 添加菜单项到应用程序菜单和上下文菜单中。
  • net 使用Chromium的原生网络库发出HTTP / HTTPS请求。
  • netLog 网络请求日志。
  • powerMonitor 监视电源状态的改变。
  • powerSaveBlocker 阻止系统进入低功耗 (休眠) 模式。
  • protocol 注册自定义协议并拦截基于现有协议的请求。
  • screen 检索有关屏幕大小、显示器、光标位置等的信息。
  • session 管理浏览器会话、cookie、缓存、代理设置等。
  • systemPreferences 获取system preferences。
  • 触控板 为原生macOS应用创建TouchBar布局。
  • Tray 添加图标和上下文菜单到系统通知区。
  • webContents 渲染以及控制 web 页面。

Renderer Process 模块

  • desktopCapturer 从桌面上捕获音频和视频的媒体源信息。
  • ipcRenderer 从渲染器进程到主进程的异步通信。
  • remote 在渲染进程中使用主进程模块。
  • webFrame 自定义渲染当前网页。

进程间通讯

渲染进程=>主进程=>渲染进程

1)异步

在主进程中

代码语言:javascript复制
const { ipcMain } = require('electron')

ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong')
})

在渲染器进程 (网页) 中

代码语言:javascript复制
const { ipcRenderer } = require('electron')

ipcRenderer.on('asynchronous-reply', (event, arg) => {
  console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')

2)同步

在主进程中

代码语言:javascript复制
const { ipcMain } = require('electron')
ipcMain.on('synchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.returnValue = 'pong'
})

在渲染器进程 (网页) 中

代码语言:javascript复制
const { ipcRenderer } = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"

主进程=>渲染进程

在主进程中

代码语言:javascript复制
const { ipcMain } = require('electron')

let rtmpWindow = new BrowserWindow({
    width: 400,
    height: 600,
    frame: false,
    hasShadow: false,
    show: false,
    title: "",
    webPreferences: {
      nodeIntegration: true
    }
  });
rtmpWindow.loadFile("views/rtmp.html");
rtmpWindow.webContents.openDevTools()


rtmpWindow.webContents.on('did-finish-load', () => {
  //下面的这行代码要在上面的BrowserWindow加载完成之后调用
  rtmpWindow.webContents.send('asynchronous-msg',"test");
})

在渲染器进程 (网页) 中

代码语言:javascript复制
const { ipcRenderer } = require('electron')

ipcRenderer.on('asynchronous-msg', (event, arg) => {
  console.log(arg) // prints "pong"
})

渲染进程=>渲染进程

使用全局共享属性

使用全局共享属性或者用 Storage API( localStoragesessionStorage 或者 IndexedDB)。

但不具备事件机制,没有实质的通信功能。

代码语言:javascript复制
// 主进程中在global上自定义对象
global.saveDefault= {
  token: 'default value',
  name: 'default value',
  password: 'default value',
}
 
// 在登录页 In page 1
require('electron').remote.getGlobal('saveDefault').token= 'token'
require('electron').remote.getGlobal('saveDefault').name= 'name'
 
 
// 在主页 In page 2 就可以获取到
console.log(require('electron').remote.getGlobal('saveDefault').name)
console.log(require('electron').remote.getGlobal('saveDefault').token)
console.log(require('electron').remote.getGlobal('saveDefault').password)
利用主进程做消息中转
代码语言:javascript复制
// 渲染进程1
ipcRenderer.send('ping-event', 'ping')

// 在主进程中
ipcMain.on('ping-event', (event, arg) => {
  yourWindow.webContents.send('pong-event', 'something');
}
 
// 渲染进程2
ipcRenderer.on('pong-event', (event, arg) => {
     // do something
  }
)
使用 ipcRenderer.sendTo()
代码语言:javascript复制
// 渲染进程
ipcRenderer.sendTo(webContentsId, channel, [, arg1][, arg2][, ...])
ipcRenderer.sendTo(winId, 'ping', 'someThing')
利用 remote 接口直接获取渲染进程发送消息
代码语言:javascript复制
// 渲染进程
// 获取窗口的id
remote.BrowserWindow.fromId(winId).webContents.send('ping', 'someThing');
获取进程id的方法

第一种: 通过 global 设置和获取

代码语言:javascript复制
// 主进程中在global上自定义对象
global.winIds= {
  win1Id : win1.id,
  win2Id : win2.id
}

// 主进程获取
global.winIds["win1Id"];

// 渲染进程获取
require('electron').remote.getGlobal('winIds').win1Id

第二种是: 主进程创建事件,发送信息(不推荐)

代码语言:javascript复制
// 主进程中
win1.webContents.send('distributeIds',{
    win2Id : win2.id
});
 
win2.webContents.send('distributeIds',{
    win1Id : win1.id
});

页面数据共享

在两个网页(渲染进程)间共享数据最简单的方法是使用浏览器中已经实现的 HTML5 API。 其中比较好的方案是用 Storage API( localStoragesessionStorage 或者 IndexedDB)。

你还可以用 Electron 内的 IPC 机制实现。将数据存在主进程的某个全局变量中,然后在多个渲染进程中使用 remote 模块来访问它。

在主进程中

代码语言:javascript复制
global.sharedObject = {
  someProperty: 'default value'
}

在第一个页面中

代码语言:javascript复制
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value';

在第二个页面中

代码语言:javascript复制
console.log(require('electron').remote.getGlobal('sharedObject').someProperty);

常见问题

jQuery/RequireJS/Meteor/AngularJS 的问题

jQuery 等新版本的框架,在 Electron 中使用普通的引入的办法会引发异常,原因是 Electron 默认启用了 Node.js 的 require 模块,而这些框架为了支持 commondJS 标准,当 Window 中存在 require 时,会启用模块引入的方式。

分别有以下几种解决方案:

方式一:

使用 Electron 官方论坛提供的方法,改变require的写法。下面的代码各个框架通用:

代码语言:javascript复制
//在引入框架之前先输入下面的代码
<script>
	window.nodeRequire = require;
	delete window.require;
	delete window.exports;
	delete window.module;
</script>

方式二:

禁用Node.js的require模块化引入(如果你不想使用 Node.js 模块):

代码语言:javascript复制
// In the main process.
let win = new BrowserWindow({
  webPreferences: {
   nodeIntegration: false
  }
});

方式三:

为使 web 项目正常浏览,在引入 jquery 后进行判断:

代码语言:javascript复制
//置于引入 jQuery 之后
<script>
  if (typeof module === 'object') {window.jQuery = window.$ = module.exports;};
</script>

关于页面跳转 的问题

在接收到命令后创建下一个窗口(创建窗口需要时间,期间可能出现空白):

代码语言:javascript复制
//在main.js中::
const ipc = require('electron').ipcMain;
//进行监控,如果有new-window 发送过来,则重新创建一个窗口,文件是list.html
ipc.on('new-window', function () {
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, '/views/list.html'),
    protocol: 'file:',
    slashes: true
  }))
});

关于无边框窗口 的问题

为了使窗口无边框,使得在某些时候让项目看起来更美观,所以在创建窗口的时候通过设置 frame 属性的值为 false 来创建无边框窗口。

但是无边框窗口会产生无法移动的问题,对于这个问题我们可以在渲染进程中通过编辑 css 文件来解决。

设置 -webkit-app-region: no-drag 禁止拖拽:

代码语言:javascript复制
body {
    -webkit-app-region: no-drag
}

需要拖拽的地方设置:

代码语言:javascript复制
section {
    -webkit-app-region: drag;
}

0 人点赞