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


}
main process 的使命完成, 主界面进行构建布局 。
在 workbench.html 中加载了 workbench.js , 这里调用 return require('vs/workbench/electron-browser/desktop.main').main(configuration);实现对主界面的展示
vs/workbench/electron-browser/desktop.main.ts创建工作区 , 调用 workbench.startup()方法 , 构建主界面展示布局
...
async open(): Promise {
const services = await this.initServices();
await domContentLoaded();
mark('willStartWorkbench');
// 1.创建工作区
const workbench = new Workbench(document.body, services.serviceCollection, services.logService);
// 2.监听窗口变化
this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true, workbench)));
// 3.工作台生命周期
this._register(workbench.onShutdown(() => this.dispose()));
this._register(workbench.onWillShutdown(event => event.join(services.storageService.close())));
// 3.启动工作区
const instantiationService = workbench.startup();
...
}
...
vs/workbench/browser/workbench.ts工作区继承自 layout 类 , 主要作用是构建工作区 , 创建界面布局 。
export class Workbench extends Layout {
...
startup(): IInstantiationService {
try {
...
// Services
const instantiationService = this.initServices(this.serviceCollection);
instantiationService.invokeFunction(async accessor => {
const lifecycleService = accessor.get(ILifecycleService);
const storageService = accessor.get(IStorageService);
const configurationService = accessor.get(IConfigurationService);
// Layout
this.initLayout(accessor);
// Registries
this.startRegistries(accessor);
// Context Keys
this._register(instantiationService.createInstance(WorkbenchContextKeysHandler));
// 注册监听事件
this.registerListeners(lifecycleService, storageService, configurationService);
// 渲染工作区
this.renderWorkbench(instantiationService, accessor.get(INotificationService) as NotificationService, storageService, configurationService);
// 创建工作区布局
this.createWorkbenchLayout(instantiationService);
// 布局构建
this.layout();
// Restore
try {
await this.restoreWorkbench(accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(ILogService), lifecycleService);
} catch (error) {
onUnexpectedError(error);
}
});
return instantiationService;
} catch (error) {
onUnexpectedError(error);
throw error; // rethrow because this is a critical issue we cannot handle properly here
}
}
...
}
5.事件分发eventsrc/vs/base/common/event.ts
程序中常见使用 once 方法进行事件绑定, 给定一个事件 , 返回一个只触发一次的事件 , 放在匿名函数返回
export function once(event: Event): Event {
return (listener, thisArgs = null, disposables?) => {
// 设置次变量 , 防止事件重复触发造成事件污染
let didFire = false;
let result: IDisposable;
result = event(e => {
if (didFire) {
return;
} else if (result) {
result.dispose();
} else {
didFire = true;
}
return listener.call(thisArgs, e);
}, null, disposables);
if (didFire) {
result.dispose();
}
return result;
};
}
循环派发了所有注册的事件 ,事件会存储到一个事件队列 , 通过 fire 方法触发事件
private _deliveryQueue?: LinkedList;//事件存储队列
fire(event: T): void {
if (this._listeners) {
// 将所有事件传入 delivery queue
// 内部/嵌套方式通过emit发出.
// this调用事件驱动
if (!this._deliveryQueue) {
this._deliveryQueue = new LinkedList();
}
for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
this._deliveryQueue.push([e.value, event]);
}
while (this._deliveryQueue.size > 0) {
const [listener, event] = this._deliveryQueue.shift()!;
try {
if (typeof listener === 'function') {
listener.call(undefined, event);
} else {
listener[0].call(listener[1], event);
}
} catch (e) {
onUnexpectedError(e);
}
}
}
}
6.进程通信主进程src/vs/code/electron-main/main.ts
main.ts 在启动应用后就创建了一个主进程 main process , 它可以通过 electron 中的一些模块直接与原生 GUI 交互 。
server = await serve(environmentService.mainIPCHandle);
once(lifecycleService.onWillShutdown)(() => server.dispose());
渲染进程仅启动主进程并不能给你的应用创建应用窗口 。 窗口是通过 main 文件里的主进程调用叫 BrowserWindow 的模块创建的 。
主进程与渲染进程之间的通信在 electron 中 , 主进程与渲染进程有很多通信的方法 。 比如 ipcRenderer 和 ipcMain , 还可以在渲染进程使用 remote 模块 。