背景
构想一个场景,当咱们在浏览一个网页并且须要下载某个资源时,你的电脑可能常常会跳出一个提示框,询问你是否须要关上“迅雷”。当咱们点击“是”,则会唤醒该本地利用进行下载工作。针对这个场景产生了一个疑难,网页是如何关上PC端利用的呢?
本文针对Electron利用在Windows零碎和Mac零碎如何唤醒进行探讨。
Electron 基础架构
Electron 是一个能够用 JavaScript、HTML 和 CSS 构建桌面应用程序的库。这些应用程序能打包到 Mac、Windows 和 Linux 零碎上运行,也能上架到 Mac 和 Windows 的 App Store。
Electron 联合了 Chromium、Node.js 以及 操作系统本地的 API(如关上文件窗口、告诉、图标等)。
Electron 与 Chromium 在架构上很类似。Chromium运行时有一个 Browser Process,以及一个或者多个 Renderer Process。
Renderer Process 顾名思义负责渲染Web页面。Browser Process 则负责管理各个 Renderer Process 以及其余局部(比方菜单栏,收藏夹等等),如下图:
在 Electron中,构造依然相似,不过这里是一个 Main Process 治理多个 Renderer Process。
而且在 Renderer Process 能够应用 Node.js 的 API,这就赋予来 Electron 极大的能力,以下是主过程以及渲染过程能够拜访到的API:
如何将 Chromium 与 Node 整合
Electron 最让人兴奋的中央在于 Chromium 与 Node 的整合。艰深的讲,咱们能够在 Chromium 的管制台上做任何 Node 能够做的事。
可能做这个整合,首先得益于 Chromium 和 Node.js 都是基于 v8 引擎来执行 js 的,所以给了一种可能,他们是能够一起工作的。
然而有一个问题,Chromium 和 Node.js 的事件循环机制不同。咱们晓得,Node.js 是基于 libuv 的,Chromium 也有一套本人的事件循环形式,要让他们一起工作,就必须整合这两个事件循环机制。
如上图所示,Electron 采纳了这样一种形式,它起了一个新的线程轮询 libuv 中的 backend fd,从而监听 Node.js 中的事件,一旦发现有新的事件产生,就会立刻把它 post 到 Chromium 的事件循环中,唤醒主线程解决这个事件。
Electron 与 NW.js 的比照以及区别
和 Electron 同样闻名的跨平台桌面利用开源库还有 NW.js。他们都有十分闻名的利用,例如用Electron开发的有 vscode,用 NW.js 开发的有钉钉。
Electron 的原名叫 Atom Shell,NW.js 的原名叫 node-webkit;他们起初是同一个作者开发,而且这个这个作者是国人,先向大佬致敬,为咱们开源这么优良的开源工具。起初种种原因分为两个产品,一个命名为 NW.js(英特尔公司提供技术支持)、 另一命名为 Electron(Github 公司提供技术支持)。
-
两者在GitHub上的数据比照
nw.js (36.7k star, 4051 commits, 256 releases, 748 open issues, 5862 closed) electron (81.7k star, 23364 commits, 849 releases, 1047 open issues, 11612 closed)
能够看出 Electron 更加沉闷。
-
两者程序的入口不同
在 NW.js 中,利用的主入口是网页或者JS脚本。 你须要在 package.json 中指定一个html或者js文件,一旦利用的主窗口(在html作为主入口点的状况下)或脚本被执行,利用就会在浏览器窗口关上。在 Electron 中,入口是一个 JavaScript 脚本。 不同于间接提供一个URL,你须要手动创立一个浏览器窗口,而后通过 API 加载 HTML 文件。 你还能够监听窗口事件,决定何时让利用退出。
Electron 的工作形式更像 Node.js 运行时 ,Electron 的 APIs 更加底层。
- Node 集成
在 NW.js,网页中的 Node 集成须要通过给 Chromium 打补丁来实现。但在 Electron 中,咱们抉择了另一种形式:通过各个平台的音讯循环与 libuv 的循环集成,防止了间接在 Chromium 上做改变。这就意味着 Electron 迭代的老本更低。
搭建一个最简略的 Electron
初始化我的项目
Electron 应用程序遵循与其余 Node.js 我的项目雷同的构造。 首先创立一个文件夹并初始化 npm 包。
mkdir my-electron-app && cd my-electron-app
npm init
init初始化命令会提醒您在我的项目初始化配置中设置一些值 为本教程的目标,有几条规定须要遵循:
- entry point 应为 main.js(其余命名也能够,对应主文件即可).
- author 与 description 可为任意值,但对于利用打包是必填项。
你的 package.json 文件应该像这样:
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT"
}
而后,将 electron 包装置到利用的开发依赖中。
最初,您心愿可能执行 Electron 如下所示,在您的 package.json配置文件中的scripts字段下减少一条start命令:
{
"scripts": {
"start": "electron ."
}
}
start命令能让您在开发模式下关上您的利用
留神:此脚本命令将通知 Electron 在您我的项目根目录运行,如果目录不对您的利用将立刻抛出一个谬误提醒您它无奈找到要运行的利用
任何 Electron 应用程序的入口都是 main 文件。 这个文件管制了主过程,它运行在一个残缺的Node.js环境中,负责管制您利用的生命周期,显示原生界面,执行非凡操作并治理渲染器过程
执行期间,Electron 将根据利用中 package.json配置下main字段中配置的值查找此文件
要初始化这个main文件,须要在您我的项目的根目录下创立一个名为main.js的空文件。
创立页面
在能够为咱们的利用创立窗口前,咱们须要先创立加载进该窗口的内容。 在 Electron 中,每个窗口中无论是本地的HTML文件还是近程URL都能够被加载显示。
此处将采纳本地HTML的形式。 在您的我的项目根目录下创立一个名为index.html的文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>,
Chromium <span id="chrome-version"></span>,
and Electron <span id="electron-version"></span>.
</body>
</html>
主文件创建界面
当初您有了一个页面,将它加载进利用窗口中。 要做到这一点,你须要 两个Electron模块:
- app 模块,它控制应用程序的事件生命周期。
-
BrowserWindow 模块,它创立和管理应用程序 窗口。
因为主过程运行着Node.js,您能够在文件头部将他们导入作为公共JS模块:const { app, BrowserWindow } = require('electron')
而后,增加一个createWindow()办法来将index.html加载进一个新的BrowserWindow实例。
function createWindow () { const win = new BrowserWindow({ width: 800, height: 600 }) win.loadFile('index.html') }
接着,调用createWindow()函数来关上您的窗口。
在 Electron 中,只有在 app 模块的 ready 事件被激发后能力创立浏览器窗口。 您能够通过应用 app.whenReady() API来监听此事件。 在whenReady()胜利后调用createWindow()。
app.whenReady().then(() => {
createWindow()
})
此时,您的电子应用程序该当胜利 关上显示您页面的窗口!
治理窗口的生命周期
尽管你当初能够关上一个浏览器窗口,但你还须要一些额定的模板代码使其看起来更像是各平台原生的。 应用程序窗口在每个OS下有不同的行为,Electron将在app中实现这些约定的责任交给开发者们。
一般而言,你能够应用 过程 全局的 platform 属性来专门为某些操作系统运行代码。
敞开所有窗口时退出利用 (Windows & Linux)
在Windows和Linux上,敞开所有窗口通常会齐全退出一个应用程序。
为了实现这一点,监听 app 模块的 ‘window-all-closed’ 事件,并在用户不是在 macOS (darwin) 上运行时调用 app.quit()
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
如果没有窗口关上则关上一个窗口 (macOS)
当 Linux 和 Windows 利用在没有窗口关上时退出了,macOS 利用通常即便在没有关上任何窗口的状况下也持续运行,并且在没有窗口可用的状况下激活利用时会关上新的窗口。
为了实现这一个性,监听 app 模块的 activate 事件,并在没有浏览器窗口关上的状况下调用你仅存的 createWindow() 办法。
因为窗口无奈在 ready 事件前创立,你该当在你的利用初始化后仅监听 activate 事件。 通过在您现有的 whenReady() 回调中附上您的事件监听器来实现这个操作。
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
此时,您的窗口控件应功能齐全!
通过预加载脚本从渲染器拜访Node.js
当初,最初要做的是输入Electron的版本号和它的依赖项到你的web页面上。
在主过程通过Node的全局 process 对象拜访这个信息是微不足道的。 然而,你不能间接在主过程中编辑DOM,因为它无法访问渲染器 文档 上下文。 它们存在于齐全不同的过程!
这是将 预加载 脚本连贯到渲染器时派上用场的中央。 预加载脚本在渲染器过程加载之前加载,并有权拜访两个 渲染器全局 (例如 window 和 document) 和 Node.js 环境。
创立一个名为 preload.js 的新脚本如下:
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})
下面的代码拜访 Node.js process.versions 对象,并运行一个根本的 replaceText 辅助函数将版本号插入到 HTML 文档中。
要将此脚本附加到渲染器流程,请在你现有的 BrowserWindow 结构器中将门路中的预加载脚本传入 webPreferences.preload 选项。
// 在文件头部引入 Node.js 中的 path 模块
const path = require('path')
// 批改现有的 createWindow() 函数
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
// ...
额定:将性能增加到您的网页内容
此刻,您可能想晓得如何为您的应用程序增加更多功能。
对于与您的网页内容的任何交互,您想要将脚本增加到您的渲染器过程中。 因为渲染器运行在失常的 Web 环境中,因而您能够在 index.html 文件敞开 </body> 标签之前增加一个 <script> 标签,来包含您想要的任意脚本:
<script src="./renderer.js"></script>
renderer.js 中蕴含的代码接下来能够应用与前端开发雷同的 JavaScript API 和工具,例如应用 webpack 打包并最小化您的代码或 React 来治理您的用户界面。
打包
1、增加Electron Forge作为应用程序的开发依赖项,并应用其import命令设置Forge的脚手架:
npm install --save-dev @electron-forge/cli
npx electron-forge import
✔ Checking your system
✔ Initializing Git Repository
✔ Writing modified package.json file
✔ Installing dependencies
✔ Writing modified package.json file
✔ Fixing .gitignore
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
Thanks for using "electron-forge"!!!
2、应用Forge的make命令创立可散发的
npm run make
> my-electron-app@1.0.0 make /my-electron-app
> electron-forge make
✔ Checking your system
✔ Resolving Forge Config
We need to package your application before we can make it
✔ Preparing to Package Application for arch: x64
✔ Preparing native dependencies
✔ Packaging Application
Making for the following targets: zip
✔ Making for target: zip - On platform: darwin - For arch: x64
Electron Forge创立包所在的out文件夹:
// Example for macOS
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
实现上述步骤后,您应该有一个功能齐全的应用程序,如下所示:
注册伪协定
主过程向渲染通信
网页唤醒electron利用
唤醒electron利用免登录流
参考文章
electron架构、集成react搭建
注册协定
发表回复