<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從 VSCode 看大型 IDE 技術(shù)架構(gòu)

          共 17095字,需瀏覽 35分鐘

           ·

          2021-05-16 16:26


          作者:paranoidjk https://zhuanlan.zhihu.com/p/96041706

          談起 Web IDE,沒人能繞開 VSCode,它非常流行,同時又完全開源,總共 350000 行 TypeScript 代碼的巨大工程,使用了 142 個開源庫。市面上選擇基于 VSCode 去修改定制的 IDE 比比皆是:Weex Studio、白鷺Egret Wing、快應(yīng)用IDE...

          我希望從 VSCode 身上看到什么?

          • 大型復(fù)雜 GUI 軟件(如 IDE 類)如何組織功能模塊代碼
          • 如何使用 Electron 技術(shù)將 Web 軟件桌面化
          • 如何在打造插件化開放生態(tài)的同時保證軟件整體質(zhì)量與性能
          • 如何打造一款好用的、流行的工具軟件

          一、VSCode 是什么

          官方定義

          https://code.visualstudio.com/

          img

          關(guān)鍵詞:

          • 代碼編輯(工具屬性)

          • 跨平臺運(yùn)行、開源

          • 核心功能:


            • IntelliSense(代碼提示)、 debugging(代碼調(diào)試)、 git(代碼管理) 都是圍繞代碼編輯的核心鏈路
            • extensions (插件)則肩負(fù)著打造開放生態(tài)的責(zé)任
          img
          img
          點(diǎn)評:
          對于工具軟件而言,需要內(nèi)心能想清楚邊界。
          哪些是自己應(yīng)該專注去做的,哪些可以外溢到交給第三方擴(kuò)展來滿足。

          發(fā)展歷程

          • 2015-04 (4年前) 發(fā)布
          • 2015-11 (發(fā)布之后半年)開源
          • 2019-05 發(fā)布 VSCode Remote Development

          團(tuán)隊負(fù)責(zé)人:Erich Gamma . JUnit 作者之一,《設(shè)計模式》作者之一, Eclipse 架構(gòu)師。2011 加入微軟,在瑞士蘇黎世組建團(tuán)隊開發(fā)基于 web 技術(shù)的編輯器,也就是后來的 monaco-editor。VSCode 開發(fā)團(tuán)隊從 10 來個人開始,早期成員大多有 Eclipse 開發(fā)團(tuán)隊的背景。

          Visual Studio Code有哪些工程方面的亮點(diǎn) 維護(hù)一個大型開源項目是怎樣的體驗? 「Shape Up」 適合中小團(tuán)隊的一種工作方式

          img

          Erich Gamma 在 GOTO 2016 發(fā)表了主題為 《The journey of visual studio code: Building an App Using JS/TypeScript, Node, Electron & 100 OSS Components》的演講,詳細(xì)講解了這個項目的發(fā)展歷程:

          沒時間觀看視頻的同學(xué)可以選擇下載完整 PDF

          PPT 的第一頁,就是 Erich Gamma 截取自己正式加入微軟之后收到的工作內(nèi)容描述的郵件:

          ”探索一種全新的和桌面 IDE 一樣成功的在線開發(fā)工具模式“

          img

          整個團(tuán)隊從大致 10 個人開始,混合老中新三代不同水平的程序員,在微軟這個巨無霸的商業(yè)公司里面想要落地這樣一個宏大的愿景是不容易的,團(tuán)隊一開始定下的思路就是像 start up 一樣工作,每月每年都要 ship 東西。

          同時他也提出早期會瘋狂的在公司內(nèi)部尋找落地場景,比如 Visual Studio Online 的在線 Code DIff 頁面,TypeScript 的官網(wǎng)的 Playground 編輯器,OneDrive 代碼文件,Edge 瀏覽器 Dev Tool 的代碼瀏覽等。

          一個重要轉(zhuǎn)折點(diǎn)是微軟本身發(fā)生的巨大變化:

          img

          伴隨微軟整個的開放開源跨平臺風(fēng)潮,Erich Gamma 敏銳的決定將產(chǎn)品從 Browser Based IDE 轉(zhuǎn)向跨平臺的 Desktop IDE,但仍然使用 Web 技術(shù),于是 electron 完美契合,VSCode 團(tuán)隊花了六個月使用 Electron 將 Web 編輯器桌面化,又花了六個月將整個 IDE 插件化,最終 VSCode 成為一個流行的產(chǎn)品同時也成為一個典型的 Electron 客戶端開源項目。

          img

          產(chǎn)品定位

          Erich Gamma 在 2017 SpringOne Platform 上有一個 關(guān)于 VSCode 的分享,講解了在他開發(fā) Eclipse 的過往經(jīng)驗基礎(chǔ)上,對 VSCode 進(jìn)行頂層設(shè)計時的諸多思路與決策,其中提到過對于 VSCode 的產(chǎn)品定位:

          img

          從圖中可以看出 VSCode 定位是處于編輯器和 IDE 的中間并且偏向輕量編輯器一側(cè)的。VSCode 的核心是“編輯器 + 代碼理解 + 調(diào)試“,圍繞這個關(guān)鍵路徑做深做透,其他東西非常克制,產(chǎn)品保持輕量與高性能。

          點(diǎn)評:
          生產(chǎn)力工具類的軟件一定要守住主線,否則很可能會變成不收門票的游樂園
          牛逼的產(chǎn)品背后一定有牛逼的團(tuán)隊,比如微軟挖到 Anders Hejlsberg,接連創(chuàng)造了 C# 和 TypeScript
          挖到 Erich Gamma,接連誕生了 monaco 和 vscode 這些明珠

          二、Electron 是什么

          上文提到 VSCode 有一個特性是跨平臺,它的跨平臺實質(zhì)是通過 electron 實現(xiàn)的。所以我們需要先簡單了解下 electron

          官方定義

          img
          img

          核心技術(shù)

          img
          • 使用 Web 技術(shù)來編寫 UI,用 chrome 瀏覽器內(nèi)核來運(yùn)行
          • 使用 NodeJS 來操作文件系統(tǒng)和發(fā)起網(wǎng)絡(luò)請求
          • 使用 NodeJS C++ Addon 去調(diào)用操作系統(tǒng)的 native API

          應(yīng)用架構(gòu)

          https://electronjs.org/docs/tutorial/application-architecture

          img
          • 1 個主進(jìn)程:一個 Electron App 只會啟動一個主進(jìn)程,它會運(yùn)行 package.json 的 main 字段指定的腳本
          • N 個渲染進(jìn)程:主進(jìn)程代碼可以調(diào)用 Chromium API 創(chuàng)建任意多個 web 頁面,而 Chromium 本身是多進(jìn)程架構(gòu),每個 web 頁面都運(yùn)行在屬于它自己的渲染進(jìn)程中

          進(jìn)程間通訊:

          • Render 進(jìn)程之間的通訊本質(zhì)上和多個 Web 頁面之間通訊沒有差別,可以使用各種瀏覽器能力如 localStorage
          • Render 進(jìn)程與 Main 進(jìn)程之間也可以通過 API 互相通訊 (ipcRenderer/ipcMain)
          點(diǎn)評:
          普通 web 頁面無法調(diào)用 native api,因此缺少一些能力
          electron 的 web 頁面所處的 Render 進(jìn)程可以將任務(wù)轉(zhuǎn)發(fā)至運(yùn)行在 NodeJS 環(huán)境的 Main 進(jìn)程,從而實現(xiàn) native API
          這套架構(gòu)大大擴(kuò)展了 electron app 相比 web app 的能力豐富度
          但同時又保留了 web 快捷流暢的開發(fā)體驗,再加上 web 本身的跨平臺優(yōu)勢
          結(jié)合起來讓 electron 成為性價比非常高的方案

          三、VSCode 技術(shù)架構(gòu)

          多進(jìn)程架構(gòu)

          img
          • 主進(jìn)程:VSCode 的入口進(jìn)程,負(fù)責(zé)一些類似窗口管理、進(jìn)程間通信、自動更新等全局任務(wù)
          • 渲染進(jìn)程:負(fù)責(zé)一個 Web 頁面的渲染
          • 插件宿主進(jìn)程:每個插件的代碼都會運(yùn)行在一個獨(dú)屬于自己的 NodeJS 環(huán)境的宿主進(jìn)程中,插件不允許訪問 UI
          • Debug 進(jìn)程:Debugger 相比普通插件做了特殊化
          • Search 進(jìn)程:搜索是一類計算密集型的任務(wù),單開進(jìn)程保證軟件整體體驗與性能

          開發(fā)流程

          • 開發(fā)文檔:https://github.com/Microsoft/vscode/wiki/How-to-Contribute
          • 主倉庫:https://github.com/microsoft/vscode
          • 其它關(guān)聯(lián)項目:https://github.com/Microsoft/vscode/wiki/Related-Projects
          # 檢出代碼
          git clone [email protected]:microsoft/vscode.git
          cd vscode

          # 安裝依賴
          yarn

          # 啟動 web 版
          yarn watch && yarn web

          # 啟動 桌面 版
          yarn watch && ./scripts/code.sh

          # 打包
          yarn run gulp vscode-[platform]
          yarn run gulp vscode-[platform]-min
          # platforms: win32-ia32 | win32-x64 | darwin | linux-ia32 | linux-x64 | linux-arm

          源碼組織

          https://github.com/microsoft/vscode/wiki/Source-Code-Organization

          下面是整個 VSCode project 的一些頂級的重點(diǎn)文件夾,后文會重點(diǎn)關(guān)注 src 與 extensions:

          ├── build         # 構(gòu)建腳本
          ├── extensions    # 內(nèi)置插件
          ├── scripts       # 工具腳本
          ├── out           # 產(chǎn)物目錄
          ├── src           # 源碼目錄
          ├── test          # 測試代碼

          VSCode 的代碼架構(gòu)也是隨著產(chǎn)品階段演進(jìn)而不斷更迭的:

          img

          下文會分享一些整個 VScode 源碼組織的一些亮點(diǎn)與特色:

          1. 隔離內(nèi)核 (src) 與插件 (extensions),內(nèi)核分層模塊化

          • /src/vs:分層和模塊化的 core
          • /src/vs/base: 通用的公共方法和公共視圖組件
          • /src/vs/code: VSCode 應(yīng)用主入口
          • /src/vs/platform:可被依賴注入的各種純服務(wù)
          • /src/vs/editor: 文本編輯器
          • /src/vs/workbench:整體視圖框架
          • /src/typings: 公共基礎(chǔ)類型
          • /extensions:內(nèi)置插件

          2. 每層按環(huán)境隔離

          內(nèi)核里面每一層代碼都會遵守 electron 規(guī)范,按不同環(huán)境細(xì)分文件夾:

          • common: 公共的 js 方法,在哪里都可以運(yùn)行的
          • browser: 只使用瀏覽器 API 的代碼,可以調(diào)用 common
          • node: 只使用 NodeJS API 的代碼,可以調(diào)用 common
          • electron-browser: 使用 electron 渲染線程和瀏覽器 API 的代碼,可以調(diào)用 common,browser,node
          • electron-main: 使用 electron 主線程和 NodeJS API 的代碼,可以調(diào)用 common, node
          • test: 測試代碼
          點(diǎn)評:
          云鳳蝶也遇到了類似問題,作為一個 低代碼 + 可視化 的研發(fā)平臺,許多功能模塊的實現(xiàn)都需要橫跨編輯態(tài)和運(yùn)行態(tài)
          如果代碼不加以限制和區(qū)分,很容易導(dǎo)致錯誤的依賴關(guān)系和預(yù)期之外的 bug
          因此云鳳蝶最終也決定采用 (editor/runtime/common) 類似的隔離架構(gòu)

          3. 內(nèi)核代碼本身也采用擴(kuò)展機(jī)制: Contrib

          可以看到 /src/vs/workbench/contrib 這個目錄下存放著非常多的 VSCode 的小的功能單元:

          ├── backup
          ├── callHierarchy
          ├── cli
          ├── codeActions
          ├── codeEditor
          ├── comments
          ├── configExporter
          ├── customEditor
          ├── debug
          ├── emmet
          ├──....中間省略無數(shù)....
          ├── watermark
          ├── webview
          └── welcome

          Contrib 有一些特點(diǎn):

          • Contrib 目錄下的所有代碼不允許依賴任何本文件夾之外的文件
          • Contrib 主要是使用 Core 暴露的一些擴(kuò)展點(diǎn)來做事情
          • 每一個 Contrib 如果要對外暴露,將API 在一個出口文件里面導(dǎo)出 eg: contrib/search/common/search.ts
          • 一個 Contrib 如果要和另一個 Contrib 發(fā)生調(diào)用,不允許使用除了出口 API 文件之外的其它文件
          • 接上一條,即使 Contrib 事實上可以調(diào)用另一個 Contrib 的出口 API,也要審慎的考慮并盡量避免兩個 Contrib 互相依賴

          VSCode 開發(fā)團(tuán)隊做這個設(shè)計的目的我猜大概是因為重型的工具軟件功能點(diǎn)實在太多,而且非常多的地方都是采用相似的模式去橫向擴(kuò)展,如果這些功能代碼直接采用原始的模塊引用的方式在 core 里面硬編碼聚合拼裝起來,是一個自頂向下的架構(gòu),對維護(hù)性的挑戰(zhàn)比較大。

          而采用暴露擴(kuò)展點(diǎn)的方式,可以將依賴關(guān)系反轉(zhuǎn),依附于擴(kuò)展點(diǎn)協(xié)議,獨(dú)立的小功能的代碼實現(xiàn)可以單獨(dú)聚合,核心模塊無需硬編碼和集成所有判斷,整體是一個松散式的架構(gòu),降低了代碼信息密度與提升維護(hù)性,也更好擴(kuò)展。

          但是 VSCode Contrib 的具體業(yè)務(wù)代碼組織其實看起來沒有太多范式,而且這個內(nèi)核代碼的擴(kuò)展機(jī)制 Contrib 和 VSCode 開放給外界的插件化機(jī)制 extension 是有差異的,讀起來十分頭疼。

          通過和兄弟團(tuán)隊 CloudIDE 開發(fā)組的專家交流,我得到兩條主要差異性:

          • extension 每一個都是運(yùn)行在歸宿于自己的獨(dú)立宿主進(jìn)程,而 contrib 的功能基本是要運(yùn)行在主進(jìn)程的
          • extension 只能依附于 core 開放的擴(kuò)展點(diǎn)而活,但是 contrib 可以通過依賴注入拿到所有 core 內(nèi)部實現(xiàn)的 class (雖然官方不推薦)

          4. 依賴注入

          上一小節(jié)提到了 VSCode 的代碼大量使用了依賴注入,這項技術(shù)的具體實現(xiàn)細(xì)節(jié)本文不會展開細(xì)講,感興趣的可以閱讀一些好的實現(xiàn):

          • 云鳳蝶團(tuán)隊同學(xué)寫的 power-di
          • 社區(qū)開源的強(qiáng)大的 http://inversify.io

          TS 依賴注入常見的實現(xiàn)原理是使用 reflect-metadata 設(shè)置與獲取元信息,從而可以實現(xiàn)在運(yùn)行時拿到本來屬于編輯態(tài)的 TypeScript 類型相關(guān)元信息,具體來說就是下面這些 API:

          • Reflect.getMetadata("design:type", , target, key): 獲取 class 屬性類型元信息
          • Reflect.getMetadata("design:paramtypes", target, key): 獲取 class 方法參數(shù)元信息
          • Reflect.getMetadata("design:returntype", target, key):獲取 class 方法返回值元信息
          • Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey):設(shè)置元信息
          • Reflect.getMetadata(metadataKey, target, propertyKey): 獲取元信息

          不過具體到 VSCode 的依賴注入,它沒有使用 reflect-metadata 這一套,而是基于 decorator 去標(biāo)注元信息,整個實現(xiàn)了一套自己的依賴注入方式,具體可以參考vscode 源碼解析-依賴注入 這篇文章,大致包含如下幾類角色:

          • Service:服務(wù)的實現(xiàn)邏輯
          • Interface:服務(wù)的接口描述
          • Client:服務(wù)使用方
          • Manger:服務(wù)管理器

          舉個例子來看,在 /src/core/platform 里面定義了大量 service,其他地方消費(fèi)者 Client 都可以用依賴注入的方式使用到,偽代碼如下:

          class Client {
          // 構(gòu)造函數(shù)參數(shù)注入(依賴注入方式的一種)
          constructor(
          // 必選
          @IModelService modelService: IModelService,
          // 可選
          @optional(IEditorService) editorService: IEditorService
          ) {
          // use services
          }
          }

          // 實例化
          instantiationService.createInstance(Client);

          5. 絕對路徑 import

          img
          img
          點(diǎn)評:
          絕對路徑 import 是一個非常值得學(xué)習(xí)的技巧,具體的方式是配置 TypeScript compilerOptions.paths
          相對路徑 import 對閱讀者的大腦負(fù)擔(dān)高,依賴當(dāng)前文件位置上下文信息才能理解
          假設(shè)修改代碼的時候移動文件位置,相對路徑需要修改本文件的所有 import,絕對路徑不需要

          6. 命令系統(tǒng)

          VSCode 和 monaco-editor 都有自己的命令系統(tǒng),螞蟻 CloudIDE 團(tuán)隊的同學(xué)也曾經(jīng)對命令系統(tǒng)的優(yōu)勢做過總結(jié):

          傳統(tǒng)的模塊調(diào)用是個網(wǎng)狀,不太好找到一個切面來理解或治理:

          img

          而命令系統(tǒng)是中心化的,各功能末端變成了扁平化的結(jié)構(gòu):

          img
          點(diǎn)評:
          云鳳蝶編輯器也遇到這類問題,云鳳蝶的自由畫布編輯器有非常多的功能,參見 《打造媲美 Sketch 的自由畫布》
          而且很多情況下一個功能的觸發(fā)方式是多種多樣的,比如大綱樹右鍵菜單觸發(fā),頂部工具欄觸發(fā),畫布右鍵菜單觸發(fā),鍵盤快捷鍵觸發(fā)等
          這就要求該功能的實現(xiàn)函數(shù)能跨區(qū)域在多個位置調(diào)用,這很容易導(dǎo)致代碼依賴關(guān)系異常混亂。
          而命令系統(tǒng)就是一種解決這個問題的很好思路

          7. TypeScript

          img
          img
          img

          啟動流程 (TLDR)

          上文初步了解了 vscode 的技術(shù)架構(gòu)與源碼組織,手癢的同學(xué)估計有點(diǎn)等不及嘗試走一遍 vscode 的啟動流程了。

          然后在正式發(fā)車之前,我需要給大家一點(diǎn)友情提醒,如果你沒耐心看完下面的 VSCode 的啟動流程,應(yīng)該知道,人生得過且過 o(╥﹏╥)o

          總體來看,VSCode 的啟動代碼真正 show 給我們看了一個復(fù)雜的客戶端軟件的代碼會工程化到什么地步,這其中摻雜了大量的基于 TypeScript 的 OOP 式的代碼組織,各種對邊界,宿主環(huán)境,上下文的處理,本來簡單的啟動 APP 渲染一個頁面流程變得極其復(fù)雜。

          下面精簡抽取核心啟動鏈路的文件和方法看一看:

          • 入口:package.json { "main": "./out/main" } : electron 的標(biāo)準(zhǔn)啟動入口
          • '/out/main.js': 是構(gòu)建產(chǎn)物的入口文件,它對應(yīng)源碼 '/src/main.js'
          // /src/main.js 的精簡核心鏈路
          const { app, protocol } = require('electron');

          app.once('ready', function () {
          // electron 啟動好之后,調(diào)用 vscode 的入口
          onReady();
          });

          async function onReady() {
          // 獲取緩存文件目錄地址和語言配置,執(zhí)行啟動
          const [cachedDataDir, nlsConfig] = await Promise.all([nodeCachedDataDir.ensureExists(), resolveNlsConfiguration()]);
          startup(cachedDataDir, nlsConfig);
          }

          function startup(cachedDataDir, nlsConfig) {
          // 先加載 vscode 自己開源的 AMD Loader https://github.com/Microsoft/vscode-loader/
          // 再使用這個 loader 去加載 VSCode 的主入口文件
          require('./bootstrap-amd').load('vs/code/electron-main/main');
          }
          // /src/vs/code/electron-main/main.ts 精簡核心鏈路

          // 初始化主類
          const code = new CodeMain();
          // 執(zhí)行主入口函數(shù)
          code.main();

          class CodeMain {
          main() {
          // vscode 的 class public 入口一般只是空殼,真正的都在 private 邏輯里面
          this.startUp();
          }
          private async startup() {
          // 先創(chuàng)建依賴的初始化 service
          const [instantiationService, instanceEnvironment] = this.createServices();
          // 創(chuàng)建編輯器實例并調(diào)用 startUp 方法
          return instantiationService.createInstance(CodeApplication).startup();
          }
          }
          // /src/vs/code/electron-main/app.ts

          export class CodeApplication extends Disposable {
          async startup(): Promise<void> {
          // IPC Server
          const electronIpcServer = new ElectronIPCServer();
          // SharedProcess
          const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
          // 創(chuàng)建一大堆依賴的 service
          // IUpdateService IWindowsMainService IDialogMainService IMenubarService IStorageMainService......
          const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient);
          // 打開一個窗口
          const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
          }
          private openFirstWindow() {
          const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
          return windowsMainService.open();
          }
          }
          // /src/vs/platform/windows/electron-main/windowsMainService.ts
          export class WindowsMainService extends Disposable implements IWindowsMainService {
          open() {
          // 執(zhí)行 open
          this.doOpen();
          }
          private doOpen() {
          // 打開瀏覽器窗口
          this.openInBrowserWindow();
          }
          private openInBrowserWindow() {
          // 創(chuàng)建窗口
          const createdWindow = window = this.instantiationService.createInstance(CodeWindow, {
          state,
          extensionDevelopmentPath: configuration.extensionDevelopmentPath,
          isExtensionTestHost: !!configuration.extensionTestsPath
          });
          }
          private doOpenInBrowserWindow() {
          // 加載頁面
          window.load(configuration);
          }
          }
          // /src/vs/code/electron-main/window.ts
          export class CodeWindow extends Disposable implements ICodeWindow {
          load() {
          // 調(diào)用 electron 的 api 加載一個 url 的 html 頁面
          this._win.loadURL(this.getUrl(configuration));
          }
          private getUrl() {
          // 獲取要打開的 url
          let configUrl = this.doGetUrl(config);
          return configUrl;
          }
          private doGetUrl(config: object): string {
          // 終于看到 html 了??!淚流滿面〒▽〒
          // 打開 VSCode 的工作臺,也就是 workbench
          return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`;
          }
          }

          代碼編輯器技術(shù)

          因為本文關(guān)注的重點(diǎn)并不在真正的代碼編輯器技術(shù)而是在調(diào)研一下大型軟件的工程化,因此本文只會簡要介紹一下代碼編輯相關(guān)的的一些核心技術(shù):

          • monaco-editor 文本編輯器,非常精深的領(lǐng)域,不展開聊,可以看一些有意思的細(xì)節(jié):


            • Text Buffer 性能優(yōu)化
            • MVVM 架構(gòu)
          • language server protocol 語言提示, 也是 vscode 的一大創(chuàng)舉:


            • 不再關(guān)注 AST 和 Parser,轉(zhuǎn)而關(guān)注 Document 和 Position,從而實現(xiàn)語言無關(guān)。
            • 將語言提示變成 CS 架構(gòu),核心抽象成當(dāng)點(diǎn)擊了文檔的第幾行第幾列位置需要 server 作出什么響應(yīng)的一個簡單模型,基于 JSON RPC 協(xié)議傳輸,每個語言都可以基于協(xié)議實現(xiàn)通用后端
          img
          img
          • Debug Adaptor Prototal: 調(diào)試協(xié)議
          img
          img
          img
          點(diǎn)評:
          monaco-editor 可以看出專家級人物的領(lǐng)域積累,屬于 VSCode 的核心競爭力
          language server protocol 和 Debug Adaptor Prototal 的設(shè)計就屬于高屋建瓴
          可以看出思想層次,把自己的東西做成標(biāo)準(zhǔn),做出生態(tài),開放共贏

          四、VSCode 插件系統(tǒng)

          理念差異

          img

          對比幾大 IDE:

          • Visual Studio / IntelliJ:不需要插件,all in one (不夠開放)
          • Eclipse: 一切皆插件 (臃腫、慢、不穩(wěn)定、體驗差)
          • VSCode:中庸之道

          VSCode 插件的強(qiáng)隔離

          img
          • 獨(dú)立進(jìn)程:VSCode plugin 代碼運(yùn)行在只屬于自己的獨(dú)立 Extension Host 宿主進(jìn)程里
          • 邏輯與視圖隔離:插件完全無法訪問 DOM 以及操作 UI,插件只能響應(yīng) VSCode Core 暴露的擴(kuò)展點(diǎn)
          • 視圖擴(kuò)展能力非常弱:VSCode 有非常穩(wěn)定的交互與視覺設(shè)計,提供給插件的 UI 上的洞(component slot)非常少且穩(wěn)定
          • 只能使用限制的組件來擴(kuò)展:VSCode 對視圖擴(kuò)展的能力限制非常強(qiáng),洞里面的 UI 是并不能隨意繪制的,只能使用一些官方提供的內(nèi)置組件,比如 TreeView 之類

          vscode 有哪些擴(kuò)展能力?https://code.visualstudio.com/api/extension-capabilities/overview

          點(diǎn)評:
          視圖擴(kuò)展的克制帶來統(tǒng)一的視覺與交互風(fēng)格,帶來好的用戶體驗,便于建立穩(wěn)定的用戶心智
          插件獨(dú)立進(jìn)程,與視圖隔離,保證整體軟件的質(zhì)量、性能、安全性

          Workbench 視圖結(jié)構(gòu)

          https://code.visualstudio.com/docs/getstarted/userinterface

          img
          • 標(biāo)題欄: Title Bar
          • 活動欄: Activity Bar
          • 側(cè)邊欄: Side Bar
          • 面板: Panal
          • 編輯器: Editor
          • 狀態(tài)欄: Status Bar

          在這個視圖結(jié)構(gòu)里面有哪些可擴(kuò)展呢?詳見 extending workbench:

          img

          插件 API 注入

          插件開發(fā)者調(diào)用 core 能力時需要引入名為 vscode 的 npm 模塊

          import * as vscode from 'vscode';

          而實際上這只是一個 vscode.d.ts 類型聲明文件,它聲明了所有插件可用的 API 類型。這些 API 的具體實現(xiàn)在 src/vs/workbench/api/common/extHost.api.impl.ts createApiFactoryAndRegisterActors

          那么具體這些 API 在 plugin 執(zhí)行上下文是何時注入的呢?其實是在插件 import 語句執(zhí)行的時候動了手腳。

          // /src/vs/workbench/api/common/extHostRequireInterceptor.ts

          class VSCodeNodeModuleFactory implements INodeModuleFactory {
          public load(_request: string, parent: URI): any {

          // get extension id from filename and api for extension
          const ext = this._extensionPaths.findSubstr(parent.fsPath);
          if (ext) {
          let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
          if (!apiImpl) {
          apiImpl = this._apiFactory(ext, this._extensionRegistry, this._configProvider);
          this._extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);
          }
          return apiImpl;
          }

          // fall back to a default implementation
          if (!this._defaultApiImpl) {
          let extensionPathsPretty = '';
          this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
          this._logService.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
          this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
          }
          return this._defaultApiImpl;
          }
          }

          vscode plugin 的 require 全部被 Microsoft/vscode-loader 劫持了,通過對 require 的 hack 將插件 API 注入到了運(yùn)行環(huán)境。

          插件開發(fā)與配置

          腳手架:https://github.com/Microsoft/vscode-generator-code 官方 demo: https://github.com/Microsoft/vscode-eslint

          npm install -g yo generator-code
          yo code

          一個插件核心就是一個配置文件:Extension Manifest JSON (package.json 里面的一個字段)

          https://code.visualstudio.com/api/references/extension-manifest

          一些關(guān)鍵配置如下:

          main:主文件入口,比如導(dǎo)出一個 activate 方法,可以接受 ctx 做一些事情

          "main""./src/extension.js",
          // extension.js
          const vscode = require("vscode");

          function activate(context) {
            console.log('Congratulations, your extension "helloworld" is now active!');

            let disposable = vscode.commands.registerCommand(
              "extension.helloWorld",
              function() {
                vscode.window.showInformationMessage("Hello World!");
              }
            );

            context.subscriptions.push(disposable);
          }

          exports.activate = activate;

          Activation Events 激活時機(jī)

          • onLanguage:包含該語言類型的文件被打開
          • onLanguage:json
          • onCommand:某個命令
          • onCommand:extension.sayHello
          • onDebug:開始調(diào)試
          • onDebugInitialConfigurations
          • onDebugResolve
          • workspaceContains:有匹配規(guī)則的文件被打開
          • workspaceContains:**/.editorconfig
          • onFileSystem:打開某個特殊協(xié)議的文件
          • onFileSystem:sftp
          • onView:某個 id 的視圖被顯示
          • onView:nodeDependencies
          • onUri:向操作系統(tǒng)注冊的 schema
          • vscode://vscode.git/init
          • onWebviewPanel:某種 viewType 的 webview 打開時
          • onWebviewPanel:catCoding
          • *:啟動就立即打開

          Contribution Points 擴(kuò)展點(diǎn)

          • contributes.configuration:本插件有哪些可供用戶配置的選項
          • contributes.configurationDefaults:覆蓋 vscode 默認(rèn)的一些編輯器配置
          • contributes.commands:向 vscode 的命令系統(tǒng)注冊一些可供用戶調(diào)用的命令
          • contributes.menus:擴(kuò)展菜單
          • ...

          五、感想

          Eric Raymond 有一本非常知名的著作 《大教堂與集市》,其中提到過一些有意思的觀點(diǎn):

          • 健壯的結(jié)構(gòu)遠(yuǎn)比精巧的設(shè)計來得重要。換句話說,結(jié)構(gòu)是第一位的,功能是第二位的。
          • 保持項目的簡單性。設(shè)計達(dá)到完美的時候,不是無法再增加?xùn)|西了,而是無法再減少東西了。

          VSCode 的一些工程上的優(yōu)秀設(shè)計,比如依賴注入、絕對路徑引用、命令系統(tǒng)對于云鳳蝶來說是可以馬上學(xué)以致用的,而 contrib 與 extension 的擴(kuò)展系統(tǒng),則非一日之功,也并不宜盲目下手。

          而事實上在嘗試打造每一個開發(fā)者都夢想的萬物皆 plugin 式的工具軟件之前,有一些通用的問題需要先冷靜下來思考:

          • 用戶核心在操作的資源是什么?
          • 用戶的關(guān)鍵路徑是什么?
          • 這個軟件的整體功能形態(tài),交互與視覺設(shè)計已經(jīng)穩(wěn)定了嗎?
          • 內(nèi)核功能區(qū)和第三方擴(kuò)展的功能域之間的界限在哪里?
          • 哪些環(huán)節(jié)可能會出現(xiàn)外溢需求需要第三方擴(kuò)展才能被滿足,不適宜官方動手做嗎?

          對 VSCode 而言:

          • 核心操作的資源是文件
          • 關(guān)鍵路徑是:打開文件 - 編輯文件 - 保存文件
          • 整體功能設(shè)計,交互與視覺設(shè)計非常穩(wěn)定
          • 內(nèi)核是文件管理與代碼編輯,多樣性的編程語言生態(tài),CICD 等衍生研發(fā)鏈路等可能會出現(xiàn)擴(kuò)展需求

          對云鳳蝶而言:

          • 核心操作的資源是頁面:(分為視圖和代碼)
          • 關(guān)鍵路徑是:打開頁面 - 編輯頁面(拖拽視圖,配置屬性,編寫代碼)- 保存頁面
          • 整體功能設(shè)計還在快速迭代中
          • 內(nèi)核是頁面的制作,多樣性的資產(chǎn)生態(tài),CI CD 等衍生研發(fā)鏈路等可能會出現(xiàn)擴(kuò)展需求

          圍繞這個思考,云鳳蝶將持續(xù)吸納優(yōu)秀的思想與架構(gòu),持續(xù)將編輯器的核心功能鏈路打磨通透,底層架構(gòu)搭建穩(wěn)定。

          六、相關(guān)資料

          • https://www.youtube.com/watch?v=uLrnQtAq5Ec
          • https://www.youtube.com/watch?v=Vs3AGfeuNKU
          • https://github.com/JChehe/blog/issues/5
          • https://electronjs.org/docs/tutorial/application-architecture
          • https://zhuanlan.zhihu.com/p/54289476
          • http://phosphorjs.github.io/
          • https://developer.egret.com/cn/docs/page/778
          • https://developer.chrome.com/extensions/getstarted
          • https://developer.alipay.com/article/8708
          • https://zhaomenghuan.js.org/blog/vscode-custom-development-basic-preparation.html



          點(diǎn)贊 、在看支持作者??

          瀏覽 105
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产最新激情视频在线 | www.艹逼 | 俺来了俺去了www色官网 | 亚洲AV成人无码久久精品麻豆 | 我想天天肏你 |