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


let thing = this._getServiceInstanceOrDescriptor(id);
if (thing instanceof SyncDescriptor) {
return this._createAndCacheServiceInstance(id, thing, _trace.branch(id, true));
} else {
_trace.branch(id, false);
return thing;
}
}
vs/code/electron-main/app.ts这里首先触发 CodeApplication.startup()方法 ,在第一个窗口打开 3 秒后成为共享进程 ,
async startup(): Promise {
...
// 1. 第一个窗口创建共享进程
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
sharedProcess.spawn(userEnv);
}, 3000)).schedule();
});
// 2. 创建app实例
const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient);
// 3. 打开一个窗口 调用
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
// 4. 窗口打开后执行生命周期和授权操作
this.afterWindowOpen();
...
//vscode结束了性能问题的追踪
if (this.environmentService.args.trace) {
this.stopTracingEventually(windows);
}
【万字详文:微软 VSCode IDE 源码分析揭秘】}
openFirstWindow 主要实现CodeApplication.openFirstWindow 首次开启窗口时 , 创建 Electron 的 IPC , 使主进程和渲染进程间通信 。 window 会被注册到 sharedProcessClient , 主进程和共享进程通信根据 environmentService 提供的参数(path,uri)调用 windowsMainService.open 方法打开窗口
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise): ICodeWindow[] {
...
// 1. 注入Electron IPC Service, windows窗口管理 , 菜单栏等服务
// 2. 根据environmentService进行参数配置
const macOpenFiles: string[] = (global).macOpenFiles;
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
const hasCliArgs = hasArgs(args._);
const hasFolderURIs = hasArgs(args['folder-uri']);
const hasFileURIs = hasArgs(args['file-uri']);
const noRecentEntry = args['skip-add-to-recently-opened'] === true;
const waitMarkerFileURI = args.wait
...
// 打开主窗口 , 默认从执行命令行中读取参数
return windowsMainService.open({
context,
cli: args,
forceNewWindow: args['new-window'] || (!hasCliArgs
}
vs/code/electron-main/windows.ts接下来到了 electron 的 windows 窗口 , open 方法在 doOpen 中执行窗口配置初始化 , 最终调用 openInBrowserWindow -> 执行 doOpenInBrowserWindow 使其打开 window , 主要步骤如下:
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
...
// New window
if (!window) {
//1.判断是否全屏创建窗口
...
// 2. 创建实例窗口
window = this.instantiationService.createInstance(CodeWindow, {
state,
extensionDevelopmentPath: configuration.extensionDevelopmentPath,
isExtensionTestHost: !!configuration.extensionTestsPath
});
// 3.添加到当前窗口控制器
WindowsManager.WINDOWS.push(window);
// 4.窗口监听器
window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own
window.win.webContents.on('devtools-reload-page', () => this.reload(window!));
window.win.webContents.on('crashed', () => this.onWindowError(window!, WindowError.CRASHED));
window.win.on('unresponsive', () => this.onWindowError(window!, WindowError.UNRESPONSIVE));
window.win.on('closed', () => this.onWindowClosed(window!));
// 5.注册窗口生命周期
(this.lifecycleService as LifecycleService).registerWindow(window);
}
...
return window;
}
doOpenInBrowserWindow 会调用 window.load 方法 在 window.ts 中实现
load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void {
...
// Load URL
perf.mark('main:loadWindow');
this._win.loadURL(this.getUrl(configuration));
...
}
private getUrl(windowConfiguration: IWindowConfiguration): string {
...
//加载欢迎屏幕的html
let configUrl = this.doGetUrl(config);
...
return configUrl;
}
//默认加载 vs/code/electron-browser/workbench/workbench.html
private doGetUrl(config: object): string {
return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`;