2.7 加载应用入口脚本
我们知道每个Electron应用都是从主进程的入口脚本开始执行的,那么Electron是如何找到并执行这个入口脚本的呢?
在Electron通过LoadEmbedderJavaScriptSource方法执行TypeScript逻辑时,有一个脚本文件完成了这个工作,它就是lib\browser\init.ts。在这个文件中有如下一段逻辑:
let packagePath = null; let packageJson = null; const searchPaths = ['app', 'app.asar', 'default_app.asar']; if (process.resourcesPath) { for (packagePath of searchPaths) { try { packagePath = path.join(process.resourcesPath, packagePath); packageJson = Module._load(path.join(packagePath, 'package.json')); break; } catch { continue; } } } if (packageJson == null) { process.nextTick(function () { return process.exit(1); }); throw new Error('Unable to find a valid app'); }
这段代码寻找Electron项目根目录下的resources子目录,检查这个目录下是否包含app子目录,如果没有则检查是否存在app.asar文件,如果还是没有找到,则检查是否存在default_app.asar文件,依旧没有找到则报异常并退出。
如果有一个命中,则读取它内部的package.json文件,关于Electron如何解析并读取asar文件内部的数据,后面会有介绍。
Electron读取package.json文件后,会根据package.json内的信息执行一系列的配置逻辑,比如设置应用的默认版本号、设置应用的默认DesktopName等。
这些工作都设置完之后,Electron把package.json配置的main脚本交给其内置Node.js执行,代码如下所示:
const Module = require('module'); const mainStartupScript = packageJson.main || 'index.js'; if (packagePath) { // Finally load app's main.js and transfer control to C++. process._firstFileName = Module._resolveFilename(path.join(packagePath, main-StartupScript), null, false); Module._load(path.join(packagePath, mainStartupScript), Module, true); } else { console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)'); console.error('This normally means you\'ve damaged the Electron package somehow'); }
上述代码中,mainStartupScript变量存放的就是主进程的入口脚本文件,如果开发者没有为package.json配置main属性,那么Electron会默认加载index.js,这和Node.js的行为是一致的。
这段代码里用到的Module对象是Node.js提供的内置模块,只不过Electron使用了它的两个内置方法_resolveFilename和_load。其中_load方法负责加载并执行开发者提供的主进程的入口脚本文件,这就是Electron应用加载入口脚本的逻辑。