【翻译 Electron】2.Quick Start 快速开始

223 阅读12分钟

前言:

这篇主要讲述如何从 0 到 0.1 创建一个可用的 Electron 应用,同时还会简述一下不同操作系统下如何进行兼容,用 Electron Forge 打包发布等内容,属于学习一门技术必备的以一 part, 看完会对 Electron 是啥,能干啥,有啥需要注意的有一个入门级别的了解。

这是 Electron 翻译的第二篇,学习最重要的应该是知道自己想要啥,我想要的其实是无障碍的看外文文档,所以会想着每天用自己的话去翻译一下感兴趣的技术文档,提高自己的阅读能力,而 Electron 刚好又是工作项目的核心技术,然后还没什么中文文档,所以就开始慢慢的翻译之路。

希望后面能给 Electron 做真正的中文翻译,这样简历也能写上个参与核心项目,也是不错的呀。

最后,翻译中会有一些单词的单独翻译,这个算是我自己的笔记,不喜欢的忽略即可,谢谢!


正文

词汇:

barebone:贫乏的

This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start

这份指引将会一步一步带领你创建一个啥也不是的 Electron Hello World 应用,最后得到的项目和electron/electron-quick-start 类似。

By the end of this tutorial, your app will open a browser window that displays a web page with information about which Chromium, Node.js, and Electron versions are running.

完成这份教程后,你的应用将会打开一个浏览器窗口,这个窗口展示了一个 web 页面,这个页面展示的是 Chromium, Node.js 和 Electron 正在运行的版本信息。

Prerequisites 先决条件

词汇:

  • prerequisite [ˌpriːˈrekwəzɪt]: if one thing is a prerequisite for another, it must happen or exist before the other thing is possible.
  • accordingly: If you consider a situation and then act accordingly, the way you act depends on the nature of the situation.

To use Electron, you need to install Node.js. We recommend that you use the latest LTS version available.

要使用 Electron,需要先下载 Node.js. 我们推荐使用最新可用的 LTS 版本

Please install Node.js using pre-built installers for your platform. You may encounter incompatibility issues with different development tools otherwise.

请使用你平台上预构建的安装程序来下载 Node. 否则你会因为不同的开发者工具,遇到不兼容的问题。

To check that Node.js was installed correctly, type the following commands in your teminal client:

检察一下 Node 已经正确安装完毕,在终端上执行下面的命令。

node -v
npm -v

The commands should print the versions of Node.js and npm accordingly

上述命令会打印出 Node 和 npm 的相应版本

Note: Since Electron embeds Node.js into its binary, the version of Node.js running your code is unrelated to the version running on your system.

注意:由于 Electron 内嵌了 Node.js, 运行代码的 Node.js 的版本和安装在自己系统上的 Node.js 的版本是不强相关的。

Create your application 创建自己应用

Scaffold the project 使用脚手架搭建项目

Electron apps follow the same general structure as other node.js projects. Start by creating a folder and initializing an npm package.

Electron 应用就和其他的 node 项目一样,遵循着相似的常规结构。从创建一个文件和初始化一个 npm 包开始

mkdir my-electron-app && cd my-electron-app
npm init

词汇:

  • interactive: An interactive computer program or electronic devices is one that allows direct communication between the user and the machine.
  • prompt: To prompt someone to do something means to make them decide to do it;
  • tutorial: A tutorial is part of a book or a computer program which helps you learn.

The interactive init command will prompt you to set some fields in your config. There are a few rules to follow for the purposes of this tutorial:

交互式命令init 会促使你在你的配置中创建一些内容。为了能够达成我们的目的(用脚手架搭建项目)在这个教程中需要遵循一些规则

  • entry point should be main.js

    • 入口需要是main.js
  • author and description can be any value, but are necessary for app packaging.

    • 作者和描述可以随便填,但是这些信息在应用打包的时候非常重要

Your package.json file should look something like this:

{
  "name": "my-electron-app",
  "version": "1.0.0",
  "main": "main.js",
  "license": "MIT",
  "author": "Talent Yu",
  "description": "Just a learning project"
}

Then, install the electron package into your app's devDependencies.

然后在应用的 devDependencies 中下载 electron 包

yarn add electron -D

Note: If you're encountering any issues with installing Electron, please refer to the Advanced Installation guide.

周知:如果你在下载 Electron 的时候遇到任何问题,请参考 Advanced Installation 指引

Finally, you want to be able to excute Electron. In the scripts field of your package.json config, add a start command like so:

最后你希望执行 Electron, 在 package.json 的 scipts 配置中,添加一个 start 命令:

{
    "scripts": {
        "start": "electron ."
    }
}

This start command will let you open your app in development mode.

这个 start 命令将会打开你的应用的开发模式

yarn start

Note: This script tells Electron to run on your project's root folder. At this stage, your app will immediately throw an error telling you that it cannot find an app to run.

周知: 这个脚本会告知 Electron 在你项目的根文件夹中运行。在这种情况下,你的应用会立刻抛出错误告诉你,它没有找到应用去执行。

Run the main process 执行主进程

词汇:

  • privileged: 特权的

The entry point of any Electron application is its main script. This script controls the main process, which runs in a full Node.js environment and is responsible for controlling your app's lifecycle, displaying native interface, performing privileged operations, and managing renderer processes(more on that later).

Electron 应用的入口文件就是他的核心脚本。这个脚本会控制主进程,这个进程会允许在整个 Node.js 的环境中,同时也负责控制你程序的生命周期,罗列原生的接口,执行特殊权限的行动,管理渲染进程(后面数量会越来越多)

词汇:

scaffolding: 脚手架

During execution, Electron will look for this script in the main field of the app's package.json config, which you should have configured during the app scaffolding step.

在执行过程中, Electron 会在应用的 package.json 配置中查找核心脚本,这个脚本需要你在应用脚手架搭建步骤中配置好。

To initialize the main script, create an empty file named main.js in the root folder of your project.

为了初始化核心脚本,在项目的根路径创建一个命名为main.js 的空文件.

Note: If you run the start script again at this point, your app will no longer throw any errors! However, it won't do anything yet because we haven't added any code into main.js

周知:如果你再次执行 start 脚本,你的应用将不再抛出任何错误。但是你也不会得到任何回复,因为你没有在 main.js 中加入任何的代码。

Create a web page 创建一个 web 页面

Before we can create a window for our application, we need to create the content that will be loaded into it. In Electron, each window displays web contents that can be loaded from either a local HTML file or a remote URL.

在我们为我们的应用创建窗口前,我们需要先创建将要输入进去的内容。在 Electron 中,每一个窗口展示 web 内容都可以通过本地 HTML 文件或者远端路径来注入。

For this tutorial, you will be doing the former. Create an index.html file in the root folder of your project:

在这个教程中,你将会创建一个模板。在你的应用根路径创建一个 index.html 文件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hello Electron</title>
</head>
<body>
  <h1> Hello Electron</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>

Note: Looking at this HTML document, you can observe that the version numbers are missing from the body text. We'll manually insert them later using JavaScript

周知:观察这个 HTML 文件,你会发现在 body 中的版本号是确实的。我们后面会慢慢的使用 JavaScript 插入进去。

Opening your web page in a browser window 在浏览器窗口中打开你的 web 页面

Now that you have a web page, load it into an application window. To do so, you'll need two Electron modules.

现在你已经有一个 web 页面了,把它加载到应用窗口中吧。为了实现这个目标,你需要两个 Electron 模块。

  • The app module, which controls your application's event lifecycle.

一个是 app 模块,这个模块控制你应用的事件生命周期。

  • The BrowserWindow module, which creates and manages application windows.

另外一个是 BrowserWindow 模块,这个模块会创建并管理应用的窗口。

Because the main process runs Node.js, you can import these as CommomJS modules at the top of file .

因为主进程是运行在 Node.js 上,你可以以 CommomJS 模块的形式,在文件的顶端注入。

const { app, BrowserWindow } = require("electron"); 

Then, add a createWindow() function that loads index.html into a new BrowserWindow instance.

然后添加一个 createWindow 函数在一个BrowserWindow 实例中加载 index.html 文件

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
  });
  win.loadFile("./index.html");
};

Next, call this createWindow() function to open your window.

下一步,调用 createWindow 函数,打开你的窗口

In Electron, browser windows can only be created after the app module's ready event is fired. You can wait for this event by using the app.whenReady() API. Call createWindow() after whenReady() resolves its Promise.

在 Electron 中,浏览器窗口只能在 app 模块中的 ready 事件已经完成后才能被创建。你可以使用 app.whenReady() API 等待事件执行完成。在 whenReady() 处理了自己的 Promise 之后执行 createWindow()方法。

app.whenReady().then(() => {
  createWindow()
})

Note: At this point, your Electron application should successfully open a window that displays your web page!

周知:截至至此,你的 Electron 应用要成功的打开窗口,展示你的 web 页面了。

Manage your window's lifecycle 管理窗口的生命周期

词汇:

Boilerplate code: 样板代码

Although you can now open a browser window, you'll need some additional boilerplate code to make it feel more native to each platform. Application windows behave differently on each OS, and Electron puts the responsibility on developers to implement these conventions in their app.

尽管你已经打开了一个浏览器窗口,你需要更多额外的样板代码来让这个窗口在每一个平台感觉更加的自然。 应用窗口在每一个操作系统上的行为是不一样的,而 Electron 的开发者有责任在他们的应用上贯彻遗留的习惯(不同系统的不同行为)。

In general, you can use the process global's platform attributes to run code specifically for certain operating systems.

通常情况下,你可以使用process全局的 platform标志确定操作系统,然后执行特定的代码。

Quit the app when all windows are closed(Window & Linux) 在所有的窗口都关闭时,终止应用。

On Windows and Linux, exiting all windows generally quits an application entirely.

在 windows 和 Linux 中, 退出所有的窗口通常会完全关闭一个应用

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

为了实现上述场景,需要监听 app 模块中的 window-all-closed 事件,如果用户不是在 macOS 操作系统时,手动执行 app.quit() 方法来关闭应用。

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});

Open a window if none are open(macOS) 如果没有需要打开的窗口时,macOS 还是要打开一个窗口

Whereas Linux and Windows app quit when they have no window open, macOS apps generally continue running even without any windows open, and activating the app when no windows are available should open a new one.

Linux 和 Windows 应用会在没有窗口打开时终止运行, macOS 应用即便没有任何可以打开的窗口,通常情况下还是会保持运行的,而当没有窗口可用的时候,激活一个应用需要打开一个新的窗口。

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

为了实现上面的特性(window 和 linux),监听 app 模块的 activate 事件,然后在没有浏览器窗口打开时,执行你创建窗口的 createWindow() 方法。

词汇:

  • From within: 从 ... 的内部

Because windows cannot be created before the ready event, you should only listen for activate events after your app is initialized. Do this attaching your event listener from within your existing whenReady callback.

因为窗口不能在 ready 事件之前创建,你只能在你的应用初始化之后监听 activate 事件。从你实现的 whenReady 回调内部,加上你的事件监听函数来实现这个目的。

app.whenReady().then(() => {
  createWindow();

  app.on('activate',() => {
    if(BrowserWindow.getAllWindows().length === 0 ) createWindow()
  })
});

Note: At this point, your window controls should be fully functional

周知:到这一步,你的窗口就能够正常可控了。

Access Node.js from the renderer with a preload script 通过渲染进程的预加载脚本进入 Node.js

Now, the last thing to do is print out the version numbers for Electron and its dependencies onto your web page.

现在要做的最后一件事,就是在你的 web 页面上打印出 Electron 和它的依赖的版本号。

词汇:

Trivial: If you describe something as trivial, you think that it is unimportant and not serious.

Accessing this information is trivial to do in the main process through Node's global process object. However, you can't just edit the DOM from the main process because it has no access to the renderer‘s document context. They're in entirely different processes!

通过 Node.js 的全局 process 对象获取这些信息是小菜一碟。然后,你不能通过主进程来编辑 DOM, 因为它无法访问渲染进程的文档上下文。它们处于完全不同的进程中。

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

周知: 如果你想更深入的了解 Electron 进程,可以查看进程模块文档。

This is where attaching a preload script to your renderer comes in handy. A preload script runs before the renderer process is loaded, and has access to both renderer globals(e.g. Window and document) and a Node.js environment.

这时依附在你渲染器的预加载脚本就有用武之地了。预加载脚本在渲染进程已经加载完毕之后运行,这个脚本能够同时访问渲染器的全局变量(比方说,window 和 document)和 Node.js 环境

Create a new script named preloaded.js as such:

创建一个新的脚本并命名为 preload.js :

window.addEventListener("DOMContentLoader", () => {
  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]);
  }
});

The above code accesses the Node.js process.versions object and runs a basic replaceText helper function to insert the version numbers into the HTML document.

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

To attach this script to your renderer process, pass in the path to your prelaod script to the webPreference.prelaod option in your existing BrowserWindow constructor.

为了将这个脚本关联到你的渲染进程,将你的预加载脚本的路径注入到 webPreference.preload 选项中,这个属性存在于 BrowserWindow 构造器中

// include the Node.js 'path' module at the top of your file
// 在文件的顶端包含有 Node.js 的 path 模块
const path = require("path");

// modify your existing createWindow() function
// 修改你已经存在的 createWindow() 函数
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
  win.loadFile("./index.html");
};
...

There are two Node.js concepts that are used here:

在这里用到了两个 Node.js 的概念

  • The __direname string points to the path of the currently executing script(in this case, your project's root folder ).

    • __dirname 字符串指向执行当前脚本的路径(在这个例子中,就是项目的根文件夹路径)
  • The path.join API joins multiple path segment together, creating a combined path string that works across all the platforms.

    • path.join 这个函数会将多个路径片段组合在一起,创建出一个合并起来的路径字符串,这个方法可以在所有的平台上使用

We use a path relative to the currently executing JavaScript file so that your relative path will work in both development and package mode.

我们使用路径关联起现在要执行的 JavaScript 文件,这样你关联起来的路径将会在开发环境和打包模式一起工作。

Bonus: Add functionality to your web contents 额外收获:为你的 web 内容添加功能

At this point, you might be wondering how to add more functionality to your application.

到这个节点,你可能想要知道怎么为你的应用添加更多的功能。

词汇:

Interaction: 交互

Arbitrary: If you describe an action,rule, or decision as arbitrary, you think that it is based on any principle, plan, or system. It often seems unfair because of this.

For any interactions with your web contents, you want to add scripts to renderer process. Because the renderer runs in a normal web environment, you can add a <script> tag right before your index.html file's closing </body> tag to include any arbitrary scripts you want.

为了页面内容的交互,你希望在渲染进程中添加脚本。因为渲染器运行在一个很普通的浏览器环境在哄,你可以在 index.html 文件的关闭标签 </body> 之前添加一个正确的 script 标签,你可以在添加你想要的任意脚本。

<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.

包含在 renderer.js 中的代码可以使用与经典的前端工程相同的 JavaScript API 和工具,比如说使用 webpack 去打包和压缩你的代码或者使用 React 来管理你的用户界面。

Recap 概述

After following the above steps, you should have a fully functional Electron application that looks like this:

通过上面的步骤,你现在应该拥有一个实用的 Electron 应用,它长这个样子:

The full code is available below:

全部可用的代码如下:

// main.js
const { app, BrowserWindow } = require("electron");

// include the Node.js 'path' module at the top of your file
// 在文件的顶端包含有 Node.js 的 path 模块
const path = require("path");

// modify your existing createWindow() function
// 修改你已经存在的 createWindow() 函数
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
  win.loadFile("./index.html");
};

app.whenReady().then(() => {
  createWindow();

  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});
// 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"]) {
    console.log(dependency,)
    replaceText(`${dependency}-version`, process.versions[dependency]);
  }
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Hello Electron</title>
  </head>
  <body>
    <h1>Hello Electron</h1>
    We are using Node.js <span id="node-version"></span> Chromium
    <span id="chrome-version"></span> and Electron
    <span id="electron-version"></span>

    <script src="./renderer.js"></script>
  </body>
</html>

To summarize all the steps we've done:

概括一下我们做的所有步骤:

  • We bootstrapped a Node.js application and added Electron as a dependency

    • 我们启动了一个 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. In this script, we used Electron's app and BrowserWindow modules to create a browser window that displays web content in a separate process(the renderer).

    • 创建了一个 main.js 脚本运行主进程,主进程是用来控制应用并运行在 Node.js 环境中。 在这个脚本中,我们使用了 Electron 的 app 和 BrowserWindow 模块去创建一个浏览器窗口,这个窗口在一个独立的进程中(渲染进程)展示浏览器内容。
  • In order to access certain Node.js functionality in the renderer, we attached a preload script to our BrowserWindow constructor.

    • 为了在渲染器中获取确定的 Node.js 方法,将预加载的脚本加载在 BrowserWindow 构造器上

Package and distribute your application 打包和分享应用

The fastest way to distribute your newly created app is using Electron Forge.

最快的分享你最新创建的应用的方法是使用 Electron Forge****

  1. Add Electron Forge as a development dependency of your app, and use its import command to set up Forge's scaffolding

添加 Electron Forge 作为应用的开发依赖,使用它的 import 命令创建一个 Forge 脚手架

yarn add --dev @electron-forge/cli
npx electron-forge import
  1. Create a distributable using Forge's make command:

通过 Forge 的 make 命令创建一个分发包

yarn make 
// 查看 package.json 的 script 可知,就是使用 elctron-forge make 

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

Electron Forge 会创建一个 out 文件夹来缓存打包的文件