万字详文:微软 VSCode IDE 源码分析揭秘( 二 )


万字详文:微软 VSCode IDE 源码分析揭秘文章插图
3.启动主进程Electron 通过 package.json 中的 main 字段来定义应用入口 。 main.js 是 vscode 的入口 。

  • src/main.js
  • _ vs/code/electron-main/main.ts
  • _ vs/code/electron-main/app.ts
  • _ vs/code/electron-main/windows.ts
  • _ vs/workbench/electron-browser/desktop.main.ts * vs/workbench/browser/workbench.ts
app.once('ready', function () {
//启动追踪 , 后面会讲到 , 跟性能检测优化相关 。
if (args['trace']) {
// @ts-ignore
const contentTracing = require('electron').contentTracing;
const traceOptions = {
categoryFilter: args['trace-category-filter'] || '*',
traceOptions: args['trace-options'] || 'record-until-full,enable-sampling'
};
contentTracing.startRecording(traceOptions, () => onReady());
} else {
onReady();
}
});
function onReady() {
perf.mark('main:appReady');
Promise.all([nodeCachedDataDir.ensureExists(), userDefinedLocale]).then(([cachedDataDir, locale]) => {
//1. 这里尝试获取本地配置信息 , 如果有的话会传递到startup
if (locale
}
if (!nlsConfiguration) {
nlsConfiguration = Promise.resolve(undefined);
}
nlsConfiguration.then(nlsConfig => {
//4. 首先会检查用户语言环境配置 , 如果没有设置默认使用英语
const startup = nlsConfig => {
nlsConfig._languagePackSupport = true;
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || '';
perf.mark('willLoadMainBundle');
//使用微软的loader组件加载electron-main/main文件
require('./bootstrap-amd').load('vs/code/electron-main/main', () => {
perf.mark('didLoadMainBundle');
});
};
// 2. 接收到有效的配置传入是其生效 , 调用startup
if (nlsConfig) {
startup(nlsConfig);
}
// 3. 这里尝试使用本地的应用程序
// 应用程序设置区域在ready事件后才有效
else {
let appLocale = app.getLocale();
if (!appLocale) {
startup({ locale: 'en', availableLanguages: {} });
} else {
// 配置兼容大小写敏感 , 所以统一转换成小写
appLocale = appLocale.toLowerCase();
// 这里就会调用config服务 , 把本地配置加载进来再调用startup
lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale).then(nlsConfig => {
if (!nlsConfig) {
nlsConfig = { locale: appLocale, availableLanguages: {} };
}
startup(nlsConfig);
});
}
}
});
}, console.error);
}
vs/code/electron-main/main.tselectron-main/main 是程序真正启动的入口,进入 main process 初始化流程.
这里主要做了两件事情:
  1. 初始化 Service
  2. 启动主实例
直接看 startup 方法的实现,基础服务初始化完成后会加载 CodeApplication, mainIpcServer, instanceEnvironment , 调用 startup 方法启动 APP
private async startup(args: ParsedArgs): Promise {
//spdlog 日志服务
const bufferLogService = new BufferLogService();
// 1. 调用 createServices
const [instantiationService, instanceEnvironment] = this.createServices(args, bufferLogService);
try {
// 1.1 初始化Service服务
await instantiationService.invokeFunction(async accessor => {
// 基础服务 , 包括一些用户数据 , 缓存目录
const environmentService = accessor.get(IEnvironmentService);
// 配置服务
const configurationService = accessor.get(IConfigurationService);
// 持久化数据
const stateService = accessor.get(IStateService);
try {
await this.initServices(environmentService, configurationService as ConfigurationService, stateService as StateService);
} catch (error) {
// 抛出错误对话框
this.handleStartupDataDirError(environmentService, error);
throw error;
}
});
// 1.2 启动实例
await instantiationService.invokeFunction(async accessor => {
const environmentService = accessor.get(IEnvironmentService);
const logService = accessor.get(ILogService);
const lifecycleService = accessor.get(ILifecycleService);
const configurationService = accessor.get(IConfigurationService);
const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleService, instantiationService, true);
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
});
} catch (error) {
instantiationService.invokeFunction(this.quit, error);