Electron 内部原理:将 Node 作为库使用
这是关于 Electron 内部原理的系列文章的第二篇。请查看 第一篇 关于事件循环集成的文章,如果你还没有看过的话。
大多数人使用 Node 用于服务器端应用程序,但由于 Node 拥有丰富的 API 集合和蓬勃发展的社区,它也非常适合作为嵌入式库。本文解释了 Node 在 Electron 中是如何作为库使用的。
构建系统
Node 和 Electron 都使用 GYP 作为它们的构建系统。如果你想将 Node 嵌入到你的应用程序中,你也必须使用它作为你的构建系统。
不熟悉 GYP?请阅读 这份指南,然后再继续阅读本文。
Node 的标志
Node 源代码目录中的 node.gyp 文件描述了 Node 的构建方式,以及许多 GYP 变量,用于控制启用 Node 的哪些部分以及是否打开某些配置。
要更改构建标志,您需要在项目的 .gypi 文件中设置变量。Node 中的 configure 脚本可以为您生成一些常见的配置,例如运行 ./configure --shared 将生成一个 config.gypi 文件,其中包含指示 Node 作为共享库构建的变量。
Electron 不使用 configure 脚本,因为它有自己的构建脚本。Node 的配置在 Electron 根源代码目录中的 common.gypi 文件中定义。
将 Node 与 Electron 链接
在 Electron 中,通过将 GYP 变量 node_shared 设置为 true,Node 被链接为共享库。因此,Node 的构建类型将从 executable 更改为 shared_library,并且包含 Node main 入点的源代码将不会被编译。
由于 Electron 使用 Chromium 附带的 V8 库,因此 Node 源代码中包含的 V8 库不会被使用。这是通过将 node_use_v8_platform 和 node_use_bundled_v8 都设置为 false 来实现的。
共享库或静态库
链接 Node 时,有两种选择:您可以将 Node 构建为静态库并将其包含在最终的可执行文件中,也可以将其构建为共享库并与最终的可执行文件一起分发。
在很长一段时间里,Electron 中 Node 都是作为静态库构建的。这使得构建过程简单,实现了最佳的编译器优化,并允许 Electron 在不额外包含 node.dll 文件的情况下分发。
然而,Chrome 切换到使用 BoringSSL 后,这种情况发生了变化。BoringSSL 是 OpenSSL 的一个分支,它去除了几个未使用的 API 并更改了许多现有的接口。由于 Node 仍然使用 OpenSSL,如果将它们链接在一起,编译器会由于冲突的符号而生成大量的链接错误。
Electron 无法在 Node 中使用 BoringSSL,也无法在 Chromium 中使用 OpenSSL,因此唯一的选择是将 Node 构建为共享库,并 隐藏 BoringSSL 和 OpenSSL 的符号 在各自的组件中。
这一改变为 Electron 带来了一些积极的副作用。在此更改之前,如果您使用了原生模块,则无法重命名 Windows 上的 Electron 可执行文件,因为可执行文件的名称在导入库中是硬编码的。在 Node 构建为共享库后,这种限制消失了,因为所有原生模块都链接到 node.dll,其名称无需更改。
支持原生模块
原生模块 在 Node 中工作方式是定义一个 Node 加载的入口函数,然后搜索 Node 中 V8 和 libuv 的符号。对于嵌入者来说,这有点麻烦,因为默认情况下,将 Node 构建为库时 V8 和 libuv 的符号是隐藏的,原生模块将无法加载,因为它们找不到这些符号。
因此,为了使原生模块工作,V8 和 libuv 的符号在 Electron 中被暴露出来。对于 V8,这是通过 强制 Chromium 的配置文件暴露所有符号 来完成的。对于 libuv,它是通过 设置 BUILDING_UV_SHARED=1 定义 来实现的。
在你的应用程序中启动 Node
经过构建和链接 Node 的所有工作后,最后一步是在您的应用程序中运行 Node。
Node 没有提供很多公共 API 来将其嵌入到其他应用程序中。通常,你可以直接调用 node::Start 和 node::Init 来启动一个新的 Node 实例。但是,如果你正在构建一个基于 Node 的复杂应用程序,你必须使用像 node::CreateEnvironment 这样的 API 来精确控制每个步骤。
在 Electron 中,Node 以两种模式启动:一种是在主进程中运行的独立模式,类似于官方 Node 二进制文件;另一种是嵌入模式,将 Node API 插入到网页中。这些细节将在未来的文章中进行解释。
