快速入门

本指南将会通过使用Electron创建一个极简的 Hello World 应用一步步的带你了解,该应用与electron/electron-quick-start类似。

通过这个教程,你的app将会打开一个浏览器窗口,来展示包含当前正在运行的 Chromium, Node.js与 Electronweb等版本信息的web界面

Prerequisites

在使用Electron进行开发之前,您需要安装 Node.js。 我们建议您使用最新的LTS版本。

请使用为你平台预构建的 Node.js 安装器来进行安装, 否则,您可能会遇到与不同开发工具不兼容的问题。

要检查 Node.js 是否正确安装,请在您的终端输入以下命令:

  1. node -v
  2. npm -v

这两个命令应输出了 Node.js 和 npm 的版本信息。

注意 因为 Electron 将 Node.js 嵌入到其二进制文件中,你应用运行时的 Node.js 版本与你系统中运行的 Node.js 版本无关。

创建你的应用程序

使用脚手架创建

Electron 应用程序遵循与其他 Node.js 项目相同的结构。 首先创建一个文件夹并初始化 npm 包。

  1. mkdir my-electron-app && cd my-electron-app
  2. npm init

init初始化命令会提示您在项目初始化配置中设置一些值 为本教程的目的,有几条规则需要遵循:

  • entry point 应为 main.js.
  • authordescription 可为任意值,但对于应用打包是必填项。

你的 package.json 文件应该像这样:

  1. {
  2. "name": "my-electron-app",
  3. "version": "1.0.0",
  4. "description": "Hello World!",
  5. "main": "main.js",
  6. "author": "Jane Doe",
  7. "license": "MIT"
  8. }

然后,将 electron 包安装到应用的开发依赖中。

  1. $ npm install --save-dev electron

注意:如果您在安装 Electron 时遇到任何问题,请 参见 高级安装 指南。

最后,您希望能够执行 Electron 如下所示,在您的 package.json配置文件中的scripts字段下增加一条start命令:

  1. {
  2. "scripts": {
  3. "start": "electron ."
  4. }
  5. }

start命令能让您在开发模式下打开您的应用

  1. npm start

注意:此脚本将告诉 Electron 在您项目根目录运行 此时,您的应用将立即抛出一个错误提示您它无法找到要运行的应用

运行主进程

任何 Electron 应用程序的入口都是 main 文件。 这个文件控制了主进程,它运行在一个完整的Node.js环境中,负责控制您应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程(稍后详细介绍)。

执行期间,Electron 将依据应用中 package.json配置下main字段中配置的值查找此文件,您应该已在应用脚手架步骤中配置。

要初始化这个main文件,需要在您项目的根目录下创建一个名为main.js的空文件。

注意:如果您此时再次运行start命令,您的应用将不再抛出任何错误! 然而,它不会做任何事因为我们还没有在main.js中添加任何代码。

创建页面

在可以为我们的应用创建窗口前,我们需要先创建加载进该窗口的内容。 在 Electron 中,每个窗口中无论是本地的HTML文件还是远程URL都可以被加载显示。

此教程中,您将采用本地HTML的方式。 在您的项目根目录下创建一个名为index.html的文件:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
  6. <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  7. <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  8. <title>Hello World!</title>
  9. </head>
  10. <body>
  11. <h1>Hello World!</h1>
  12. We are using Node.js <span id="node-version"></span>,
  13. Chromium <span id="chrome-version"></span>,
  14. and Electron <span id="electron-version"></span>.
  15. </body>
  16. </html>

注意:在这个HTML文本中,您会发现主体文本中丢失了版本编号。 稍后我们将使用 JavaScript 动态插入它们。

在窗口中打开您的页面

现在您有了一个页面,将它加载进应用窗口中。 要做到这一点,你需要 两个Electron模块:

  • app 模块,它控制应用程序的事件生命周期。
  • BrowserWindow 模块,它创建和管理应用程序 窗口。

因为主进程运行着Node.js,您可以在文件头部将他们导入作为公共JS模块:

  1. const { app, BrowserWindow } = require('electron')

然后,添加一个createWindow()方法来将index.html加载进一个新的BrowserWindow实例。

  1. function createWindow () {
  2. const win = new BrowserWindow({
  3. width: 800,
  4. height: 600
  5. })
  6. win.loadFile('index.html')
  7. }

接着,调用createWindow()函数来打开您的窗口。

在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 您可以通过使用 app.whenReady() API来监听此事件。 在whenReady()成功后调用createWindow()

  1. app.whenReady().then(() => {
  2. createWindow()
  3. })

注意:此时,您的电子应用程序应当成功 打开显示您页面的窗口!

管理窗口的生命周期

虽然你现在可以打开一个浏览器窗口,但你还需要一些额外的模板代码使其看起来更像是各平台原生的。 应用程序窗口在每个OS下有不同的行为,Electron将在app中实现这些约定的责任交给开发者们。

一般而言,你可以使用 进程 全局的 platform 属性来专门为某些操作系统运行代码。

关闭所有窗口时退出应用 (Windows & Linux)

在Windows和Linux上,关闭所有窗口通常会完全退出一个应用程序。

To implement this, listen for the app module’s 'window-all-closed' event, and call app.quit() if the user is not on macOS (darwin).

  1. app.on('window-all-closed', function () {
  2. if (process.platform !== 'darwin') app.quit()
  3. })

如果没有窗口打开则打开一个窗口 (macOS)

当 Linux 和 Windows 应用在没有窗口打开时退出了,macOS 应用通常即使在没有打开任何窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口。

To implement this feature, listen for the app module’s activate event, and call your existing createWindow() method if no browser windows are open.

因为窗口无法在 ready 事件前创建,你应当在你的应用初始化后仅监听 activate 事件。 通过在您现有的 whenReady() 回调中附上您的事件监听器来完成这个操作。

  1. app.whenReady().then(() => {
  2. createWindow()
  3. app.on('activate', function () {
  4. if (BrowserWindow.getAllWindows().length === 0) createWindow()
  5. })
  6. })

注意:此时,您的窗口控件应功能齐全!

通过预加载脚本从渲染器访问Node.js。

现在,最后要做的是输出Electron的版本号和它的依赖项到你的web页面上。

在主进程通过Node的全局 process 对象访问这个信息是微不足道的。 然而,你不能直接在主进程中编辑DOM,因为它无法访问渲染器 文档 上下文。 它们存在于完全不同的进程!

Note: If you need a more in-depth look at Electron processes, see the Process Model document.

这是将 预加载 脚本连接到渲染器时派上用场的地方。 预加载脚本在渲染器进程加载之前加载,并有权访问两个 渲染器全局 (例如 windowdocument) 和 Node.js 环境。

创建一个名为 preload.js 的新脚本如下:

  1. window.addEventListener('DOMContentLoaded', () => {
  2. const replaceText = (selector, text) => {
  3. const element = document.getElementById(selector)
  4. if (element) element.innerText = text
  5. }
  6. for (const dependency of ['chrome', 'node', 'electron']) {
  7. replaceText(`${dependency}-version`, process.versions[dependency])
  8. }
  9. })

上面的代码访问 Node.js process.versions 对象,并运行一个基本的 replaceText 辅助函数将版本号插入到 HTML 文档中。

要将此脚本附加到渲染器流程,请在你现有的 BrowserWindow 构造器中将路径中的预加载脚本传入 webPreferences.preload 选项。

  1. // 在文件头部引入 Node.js 中的 path 模块
  2. const path = require('path')
  3. // 修改现有的 createWindow() 函数
  4. function createWindow () {
  5. const win = new BrowserWindow({
  6. width: 800,
  7. height: 600,
  8. webPreferences: {
  9. preload: path.join(__dirname, 'preload.js')
  10. }
  11. })
  12. win.loadFile('index.html')
  13. }
  14. // ...

这里使用了两个Node.js概念:

  • The __dirname string points to the path of the currently executing script (in this case, your project’s root folder).
  • The path.join API joins multiple path segments together, creating a combined path string that works across all platforms.

我们使用一个相对当前正在执行JavaScript文件的路径,这样您的相对路径将在开发模式和打包模式中都将有效。

额外:将功能添加到您的网页内容

此刻,您可能想知道如何为您的应用程序添加更多功能。

对于与您的网页内容的任何交互,您想要将脚本添加到您的渲染器进程中。 由于渲染器运行在正常的 Web 环境中,因此您可以在 index.html 文件关闭 </body> 标签之前添加一个 <script> 标签,来包括您想要的任意脚本:

  1. <script src="./renderer.js"></script>

The code contained in renderer.js can then use the same JavaScript APIs and tooling you use for typical front-end development, such as using webpack to bundle and minify your code or React to manage your user interfaces.

回顾

完成上述步骤后,您应该有一个功能齐全的Electron程序,如下所示:

最简的 Electron 应用程序

完整代码如下:

  1. // main.js
  2. // Modules to control application life and create native browser window
  3. const { app, BrowserWindow } = require('electron')
  4. const path = require('path')
  5. function createWindow () {
  6. // Create the browser window.
  7. const mainWindow = new BrowserWindow({
  8. width: 800,
  9. height: 600,
  10. webPreferences: {
  11. preload: path.join(__dirname, 'preload.js')
  12. }
  13. })
  14. // and load the index.html of the app.
  15. mainWindow.loadFile('index.html')
  16. // Open the DevTools.
  17. // mainWindow.webContents.openDevTools()
  18. }
  19. // This method will be called when Electron has finished
  20. // initialization and is ready to create browser windows.
  21. // 部分 API 在 ready 事件触发后才能使用。
  22. app.whenReady().then(() => {
  23. createWindow()
  24. app.on('activate', function () {
  25. // On macOS it's common to re-create a window in the app when the
  26. // dock icon is clicked and there are no other windows open.
  27. if (BrowserWindow.getAllWindows().length === 0) createWindow()
  28. })
  29. })
  30. // Quit when all windows are closed, except on macOS. There, it's common
  31. // for applications and their menu bar to stay active until the user quits
  32. // explicitly with Cmd + Q.
  33. app.on('window-all-closed', function () {
  34. if (process.platform !== 'darwin') app.quit()
  35. })
  36. // In this file you can include the rest of your app's specific main process
  37. // code. 也可以拆分成几个文件,然后用 require 导入。
  1. // preload.js
  2. // All of the Node.js APIs are available in the preload process.
  3. // 它拥有与Chrome扩展一样的沙盒。
  4. window.addEventListener('DOMContentLoaded', () => {
  5. const replaceText = (selector, text) => {
  6. const element = document.getElementById(selector)
  7. if (element) element.innerText = text
  8. }
  9. for (const dependency of ['chrome', 'node', 'electron']) {
  10. replaceText(`${dependency}-version`, process.versions[dependency])
  11. }
  12. })
  1. <!--index.html-->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="UTF-8">
  6. <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
  7. <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  8. <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  9. <title>Hello World!</title>
  10. </head>
  11. <body>
  12. <h1>Hello World!</h1>
  13. We are using Node.js <span id="node-version"></span>,
  14. Chromium <span id="chrome-version"></span>,
  15. and Electron <span id="electron-version"></span>.
  16. <!-- You can also require other files to run in this process -->
  17. <script src="./renderer.js"></script>
  18. </body>
  19. </html>

总结我们所做的所有步骤:

*我们启动了一个Node.js程序,并将Electron添加为依赖。

  • We created a main.js script that runs our main process, which controls our app and runs in a Node.js environment. 在此脚本中, 我们使用 Electron 的 appBrowserWindow 模块来创建一个浏览器窗口,在一个单独的进程(渲染器)中显示网页内容。

  • 为了访问渲染器中的Node.js的某些功能,我们在 BrowserWindow 的构造函数上附加了一个预加载脚本。

打包并分发您的应用程序

最快捷的打包方式是使用 Electron Forge

  1. 将 Electron Forge 添加到您应用的开发依赖中,并使用其”import”命令设置 Forge 的脚手架:

    1. npm install --save-dev @electron-forge/cli
    2. npx electron-forge import
    3. Checking your system
    4. Initializing Git Repository
    5. Writing modified package.json file
    6. Installing dependencies
    7. Writing modified package.json file
    8. Fixing .gitignore
    9. We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
    10. Thanks for using "electron-forge"!!!
  2. 使用 Forge 的 make 命令来创建可分发的应用程序:

    1. npm run make
    2. > my-electron-app@1.0.0 make /my-electron-app
    3. > electron-forge make
    4. Checking your system
    5. Resolving Forge Config
    6. We need to package your application before we can make it
    7. Preparing to Package Application for arch: x64
    8. Preparing native dependencies
    9. Packaging Application
    10. Making for the following targets: zip
    11. Making for target: zip - On platform: darwin - For arch: x64

    Electron Forge creates the out folder where your package will be located:

    1. // Example for macOS
    2. out/
    3. ├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
    4. ├── ...
    5. └── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app