Electron 脚手架推荐


前言

官方是有个 forge 的脚手架,但是文档还不够全面,目前推荐使用 erb:electron-react-boilerplate/electron-react-boilerplate: A Foundation for Scalable Cross-Platform Apps

双层 package.json

这是 electron-builder 约定的, Two package.json Structure - electron-builder, 但是让人很困惑: Confused by 2 package.json structure · Issue #600 · electron-userland/electron-builder, erb 给的说法还是挺直接的,Adding Dependencies | Electron React Boilerplate

  • 优先安装在最外层的 package.jsondependencies
  • 除了 native modules, or modules with native dependencies or peer dependencies. Native modules, or packages with native dependencies, 这些应该放在内层的 package.json
  • 编译,测试等, 都放在最外层 package.jsondevDependenciese

什么时候 rebuild ?

所有的 dependences 包, 优先还是在最外层安装, erb 会调用 postinstall 脚本去检测是否是 native dependencies, 如果是的话, 会提示:

should be installed inside of the "./release/app" folder.
First, uninstall the packages from "./package.json":
Then, instead of installing the package to the root "./package.json", install the package to "./release/app/package.json"
  • 检测的逻辑是: 一些 native 模块,比如 node-sass, 包目录下会包含 binding.gyp 文件, check-native-dep.js 脚本能探测出来。
  • release/app/package.json 中定义的 dependence, 是会触发 postinstall 脚本:执行npm run rebuild && npm run link-modules

内层 package.json 编译的逻辑是怎么样的?

native dependencies 是被主进程使用的,所以不是所有的包,都能放在内层 package.json 中。

  • 首先需要重新编译的模块,在成功安装后, 先执行 npm run rebuild, 触发 electron-rebuild
  • 然后触发 npm run link-modules, 把内存编译好的模块,链接到外层的 src/node_modules 目录中, src 下的代码, 就能找到对应的包了
  • 整体项目构建的时候, webpack.base.ts 中会把内层的 app/package.jsondependencies 作为构建的 externals, 期待外部注入。
  • 构建后的 native dependencies, 会被放在 release/app/node_modules 目录下,也就是 erb 打包生成的目标目录, 最终会被 electron-builder 打包进去。指定了 directories.apprelease/app
  • 最终被压缩到 app.asar 中, 包含的文件,是被配置在了 build.files 中。
  • electron renderer 是支持 require 的, 所以能从 node_modules 里面加载对应 externals 的包。

开发时候的一些入口文件

main.ts

  • start:main: 通过 ts-node 直接编译, 然后被 electron 调用
  • build:main, 这是 electron 调用的入口, 运行的环境就是 node, 会创建窗口,加载 renderer 脚本以及对应的 preload 脚本。最终输出到 release/app/dist/main 目录中。

preload.ts

  • start:preload: 开发环境下,把 preload 编译到 .erb/dll 目录下
  • build:main: 生产环境下,同时编译 mainpreload 文件

注入到窗口中的 preload, 负责 IPC 通讯,开发环境下,会从 .erb/dll 下加载。生产环境,是和 main.ts 在同级。

  mainWindow = new BrowserWindow({
    show: false,
    width: 1024,
    height: 728,
    icon: getAssetPath('icon.png'),
    webPreferences: {
      preload: app.isPackaged
        ? path.join(__dirname, 'preload.js')
        : path.join(__dirname, '../../.erb/dll/preload.js'),
    },
  });

renderer.ts

renderer 页面中加载的。

  • start:renderer: 强制依赖 dll, 需要先构建 dll, 然后才能启动 renderer, html 页面中,会注入 DLL 的脚本
  • build:renderer:所有依赖全部打包,所以无须 dll

dll

这是为了开发环境的一种优化。把一些不需要经常变动的包,提前编译好,放在 .erb/dll 目录下,开发环境下,直接从这里加载,不需要每次都编译。

编译的实际,是在 postinstallpackage 的时候, 或者手动触发 build:dll, 输出内容到 .erb/dll 目录。

开发环境下,走 webpack.config.renderer.dev.dll.ts, 提取 外层 package.json 中的 dependencies, 生成 dll

生产环境下, 直接在 build:renderer 中完成构建, 不需要 dll 了。

开发阶段

yarn run start 会启动 yarn run start:renderer, 它的 webpack.config.renderer.dev.ts 中, 有 setupMiddlewares 继续调用: start:preloadstart:main.

跨平台编译

如果项目没有特定平台的依赖需要编译的话, 是可以跨平台编译的, 比如在 Mac 上编译 Windows 的版本。

但是由于需要 code sign 等操作, 一般还是需要在对应平台上编译。

技术总结

要了解是否需要 native 编译的包, 不需要的话,构建就可以跨平台了。不过一般而言,还是会区分 Mac 和 Windows 的构建,因为需要 code sign 等操作。