第一篇: vscode源碼分析【一】從源碼運行vscode
第二篇:vscode源碼分析【二】程序的啟動邏輯,第一個窗口是如何創建的
第三篇:vscode源碼分析【三】程序的啟動邏輯,性能問題的追蹤
第四篇:vscode源碼分析【四】程序啟動的邏輯,最初創建的服務
第五篇:vscode源碼分析【五】事件分發機制
第六篇:vscode源碼分析【六】服務實例化和單例的實現
第七篇:vscode源碼分析【七】主進程啟動消息通信服務
第八篇:vscode源碼分析【八】加載第一個畫面
在上一節中,我們講到加載第一個畫面時,加載了一個workbench.js
(src\vs\code\electron-browser\workbench\workbench.js)
這個文件中執行了:
bootstrapWindow.load([
'vs/workbench/workbench.main',
'vs/nls!vs/workbench/workbench.main',
'vs/css!vs/workbench/workbench.main'
]
加載了workbench.main,這個文件負責初始化界面需要用到的庫
它本身不負責執行任何邏輯,但卻加載了三百多個類,哈!
bootstrapWindow.load的回調方法里,執行了:
require('vs/workbench/electron-browser/main').main(configuration);
這句代碼很重要
我們看看這個類的main方法;它執行了:
const renderer = new CodeRendererMain(configuration);
return renderer.open();
CodeRendererMain類也在同一個文件里
(src\vs\workbench\electron-browser\main.ts)
它的構造函數里做了一些初始化工作(界面縮放事件設置、文件讀寫庫的設置等)
不重要,先不理會,先看open方法:
this.workbench = new Workbench(document.body, services.serviceCollection, services.logService);
//......
const instantiationService = this.workbench.startup();
你看到,我們把body傳給了workbench的實例
workbench的構造函數里,並沒有用這個body做什么事情;
而是把他傳遞給了它的父類:Layout(src\vs\workbench\browser\layout.ts),存儲在父類parent屬性里
這個類很重要,我們待會兒會說;
現在我們看看workbench的startup方法
// Layout
this.initLayout(accessor);
// Registries
this.startRegistries(accessor);
// Context Keys
this._register(instantiationService.createInstance(WorkbenchContextKeysHandler));
// Register Listeners
this.registerListeners(lifecycleService, storageService, configurationService);
// Render Workbench
this.renderWorkbench(instantiationService, accessor.get(INotificationService) as NotificationService, storageService, configurationService);
// Workbench Layout
this.createWorkbenchLayout(instantiationService);
// Layout
this.layout();
initLayout方法,初始化了一堆服務(environmentService,lifecycleService等),監聽了一堆事件(全屏、編輯器顯隱等)
renderWorkbench方法(最重要!),給body和一個叫container的元素加了一系列的樣式;
container元素是在父類Layout里初始化的,這個元素最終會是所有組件的父親;
private _container: HTMLElement = document.createElement('div');
get container(): HTMLElement { return this._container; }
之后,給container元素加了幾個子元素:
[
{ id: Parts.TITLEBAR_PART, role: 'contentinfo', classes: ['titlebar'] },
{ id: Parts.ACTIVITYBAR_PART, role: 'navigation', classes: ['activitybar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] },
{ id: Parts.SIDEBAR_PART, role: 'complementary', classes: ['sidebar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] },
{ id: Parts.EDITOR_PART, role: 'main', classes: ['editor'], options: { restorePreviousState: this.state.editor.restoreEditors } },
{ id: Parts.PANEL_PART, role: 'complementary', classes: ['panel', this.state.panel.position === Position.BOTTOM ? 'bottom' : 'right'] },
{ id: Parts.STATUSBAR_PART, role: 'contentinfo', classes: ['statusbar'] }
].forEach(({ id, role, classes, options }) => {
const partContainer = this.createPart(id, role, classes);
if (!configurationService.getValue('workbench.useExperimentalGridLayout')) { this.container.insertBefore(partContainer, this.container.lastChild);
}
this.getPart(id).create(partContainer, options);
});
這幾個子元素分別是最左側的ACTIVITYBAR_PART,中間的EDITOR_PART,等等(注意:窗口的菜單欄也是他自己渲染的)
這些元素創建出來之后,就加入到container里去了;
然后把container加入到body里去了(parent存的是body)
this.parent.appendChild(this.container);
在startup方法里還調用了this.layout()方法
position(this.container, 0, 0, 0, 0, 'relative');
size(this.container, this._dimension.width, this._dimension.height);
// Layout the grid widget
this.workbenchGrid.layout(this._dimension.width, this._dimension.height);
// Layout grid views
this.layoutGrid();
在這里把container放到到最大,占據整個body
至此界面主要元素渲染完成!
另外:
想調試界面里的內容,就不能用第一節講的調試方法來調試了;
你可以運行:
.\scripts\code.bat
先啟動畫面,然后按Ctrl+Shift+i打開調試窗口;
如果需要刷新畫面的話,可以按Ctrl+R刷新畫面;