Electron 内部机制:使用 Node 作为库
这是解释 Electron 内部原理的系列文章的第二篇。如果你还没有查看过有关事件循环集成的 第一篇文章,请查看。
¥This is the second post in an ongoing series explaining the internals of Electron. Check out the first post about event loop integration if you haven't already.
大多数人将 节点 用于服务器端应用,但由于 Node 拥有丰富的 API 集和蓬勃发展的社区,它也非常适合用作嵌入式库。本文解释了如何在 Electron 中将 Node 用作库。
¥Most people use Node for server-side applications, but because of Node's rich API set and thriving community, it is also a great fit for an embedded library. This post explains how Node is used as a library in Electron.
构建系统
¥Build system
Node 和 Electron 都使用 GYP
作为构建系统。如果你想在应用中嵌入 Node,你也必须将其用作构建系统。
¥Both Node and Electron use GYP
as their build systems. If you want to embed
Node inside your app, you have to use it as your build system too.
GYP
新手?在继续阅读本文之前,请先阅读 本指南。
¥New to GYP
? Read this guide before you continue further in this post.
Node 的标志
¥Node's flags
Node 源代码目录中的 node.gyp
文件描述了 Node 的构建方式,以及许多 GYP
变量,用于控制 Node 的哪些部分可用以及是否启用某些配置。
¥The node.gyp
file in Node's source code directory describes how Node
is built, along with lots of GYP
variables controlling which parts of
Node are enabled and whether to open certain configurations.
要更改构建标志,你需要在项目的 .gypi
文件中设置变量。Node 中的 configure
脚本可以为你生成一些常用配置,例如,运行 ./configure --shared
将生成一个 config.gypi
,其中包含指示将 Node 构建为共享库的变量。
¥To change the build flags, you need to set the variables in the .gypi
file of
your project. The configure
script in Node can generate some common
configurations for you, for example running ./configure --shared
will generate
a config.gypi
with variables instructing Node to be built as a shared library.
Electron 不使用 configure
脚本,因为它有自己的构建脚本。Node 的配置定义在 Electron 根源代码目录下的 common.gypi
文件中。
¥Electron does not use the configure
script since it has its own build scripts.
The configurations for Node are defined in the common.gypi
file
in Electron's root source code directory.
将 Node 与 Electron 链接
¥Link Node with Electron
在 Electron 中,通过将 GYP
变量 node_shared
设置为 true
,Node 将被链接为共享库,因此 Node 的构建类型将从 executable
更改为 shared_library
,并且包含 Node main
入口点的源代码将不会被编译。
¥In Electron, Node is being linked as a shared library by setting the GYP
variable node_shared
to true
, so Node's build type will be changed from
executable
to shared_library
, and the source code containing the Node's main
entry point will not be compiled.
由于 Electron 使用 Chromium 自带的 V8 库,因此不会使用 Node 源代码中包含的 V8 库。这是通过将 node_use_v8_platform
和 node_use_bundled_v8
都设置为 false
来实现的。
¥Since Electron uses the V8 library shipped with Chromium, the V8 library
included in Node's source code is not used. This is done by setting both
node_use_v8_platform
and node_use_bundled_v8
to false
.
共享库或静态库
¥Shared library or static library
与 Node 链接时,有两种选择:你可以将 Node 构建为静态库并将其包含在最终的可执行文件中,也可以将其构建为共享库并将其与最终的可执行文件一起发布。
¥When linking with Node, there are two options: you can either build Node as a static library and include it in the final executable, or you can build it as a shared library and ship it alongside the final executable.
在 Electron 中,Node 长期以来都是作为静态库构建的。这使得构建变得简单,启用了最佳的编译器优化,并且允许 Electron 无需额外的 node.dll
文件即可分发。
¥In Electron, Node was built as a static library for a long time. This made the
build simple, enabled the best compiler optimizations, and allowed Electron to
be distributed without an extra node.dll
file.
然而,在 Chrome 切换到使用 BoringSSL 后,情况发生了变化。BoringSSL 是 OpenSSL 的一个分支,它删除了一些未使用的 API,并更改了许多现有接口。由于 Node 仍然使用 OpenSSL,如果将符号链接在一起,编译器会由于符号冲突而生成大量链接错误。
¥However, this changed after Chrome switched to use BoringSSL. BoringSSL is a fork of OpenSSL that removes several unused APIs and changes many existing interfaces. Because Node still uses OpenSSL, the compiler would generate numerous linking errors due to conflicting symbols if they were linked together.
Electron 无法在 Node 中使用 BoringSSL,也无法在 Chromium 中使用 OpenSSL,因此唯一的选择是将 Node 构建为共享库,并在各自的组件中使用 隐藏 BoringSSL 和 OpenSSL 符号。
¥Electron couldn't use BoringSSL in Node, or use OpenSSL in Chromium, so the only option was to switch to building Node as a shared library, and hide the BoringSSL and OpenSSL symbols in the components of each.
此更改给 Electron 带来了一些积极的副作用。在此更改之前,如果你使用原生模块,则无法在 Windows 上重命名 Electron 的可执行文件,因为可执行文件的名称在导入库中是硬编码的。在 Node 构建为共享库后,此限制就消失了,因为所有原生模块都链接到了 node.dll
,其名称无需更改。
¥This change brought Electron some positive side effects. Before this
change, you could not rename the executable file of Electron on Windows if you
used native modules because the name of the executable was hard coded in the
import library. After Node was built as a shared library, this limitation was gone
because all native modules were linked to node.dll
, whose name didn't need to
be changed.
支持原生模块
¥Supporting native modules
原生模块 在 Node 中的工作方式是定义一个 Node 加载的入口函数,然后从 Node 中搜索 V8 和 libuv 的符号。这对嵌入器来说有点麻烦,因为默认情况下,在将 Node 构建为库时,V8 和 libuv 的符号是隐藏的,原生模块将无法加载,因为它们找不到这些符号。
¥Native modules in Node work by defining an entry function for Node to load, and then searching the symbols of V8 and libuv from Node. This is a bit troublesome for embedders because by default the symbols of V8 and libuv are hidden when building Node as a library and native modules will fail to load because they cannot find the symbols.
因此,为了让原生模块正常工作,V8 和 libuv 符号在 Electron 中被公开。对于 V8,此操作由 强制公开 Chromium 配置文件中的所有符号 完成。对于 libuv,它由 设置 BUILDING_UV_SHARED=1
定义 实现。
¥So in order to make native modules work, the V8 and libuv symbols
were exposed in Electron. For V8 this is done by
forcing all symbols in Chromium's configuration file to be exposed. For libuv,
it is achieved by setting the BUILDING_UV_SHARED=1
definition.
在你的应用中启动 Node
¥Starting Node in your app
完成所有构建和链接 Node 的工作后,最后一步就是在你的应用中运行 Node。
¥After all the work of building and linking with Node, the final step is to run Node in your app.
Node 不提供太多用于嵌入到其他应用中的公共 API。通常,只需调用 node::Start
和 node::Init
即可启动一个新的 Node 实例。然而,如果你正在基于 Node 构建一个复杂的应用,则必须使用像 node::CreateEnvironment
这样的 API 来精确控制每个步骤。
¥Node doesn't provide many public APIs for embedding itself into other apps.
Usually, you can just call node::Start
and node::Init
to start
a new instance of Node. However, if you are building a complex app based on Node,
you have to use APIs like node::CreateEnvironment
to precisely control every
step.
在 Electron 中,Node 以两种模式启动:独立模式在主进程中运行,类似于官方 Node 二进制文件;嵌入式模式将 Node API 插入到网页中。相关细节将在后续文章中解释。
¥In Electron, Node is started in two modes: the standalone mode that runs in the main process, which is similar to official Node binaries, and the embedded mode which inserts Node APIs into web pages. The details of this will be explained in a future post.