從VSCode 看大型IDE 技術架構

blank

從VSCode 看大型IDE 技術架構

零、前言

為什麼要去看VSCode?
因為我們團隊在做的中後台快速研發平台雲鳳蝶也是一款類似Web IDE形態的產品:

blank

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

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

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

一、VSCode 是什麼

官方定義

code.visualstudio.com/

blank

關鍵詞:

  • 代碼編輯(工具屬性)
  • 跨平台運行、開源
  • 核心功能:
    • IntelliSense(代碼提示)、 debugging(代碼調試)、 git(代碼管理) 都是圍繞代碼編輯的核心鏈路
    • extensions (插件)則肩負著打造開放生態的責任

blank
blank
点评:对于工具软件而言,需要内心能想清楚边界。哪些是自己应该专注去做的,哪些可以外溢到交给第三方扩展来满足。

發展歷程

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

團隊負責人: Erich Gamma . JUnit作者之一, 《設計模式》作者之一, Eclipse架構師。 2011 加入微軟,在瑞士蘇黎世組建團隊開發基於web 技術的編輯器,也就是後來的monaco-editor。 VSCode 開發團隊從10 來個人開始,早期成員大多有Eclipse 開發團隊的背景。

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

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

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

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

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

blank

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

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

一個重要轉折點是微軟本身發生的巨大變化:

blank

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

blank

產品定位

Erich Gamma在2017 SpringOne Platform上有一個關於VSCode的分享,講解了在他開發Eclipse的過往經驗基礎上,對VSCode進行頂層設計時的諸多思路與決策,其中提到過對於VSCode的產品定位:

blank

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

点评:生产力工具类的软件一定要守住主线,否则很可能会变成不收门票的游乐园牛逼的产品背后一定有牛逼的团队,比如微软挖到Anders Hejlsberg,接连创造了C# 和TypeScript挖到Erich Gamma,接连诞生了monaco 和vscode 这些明珠

二、Electron 是什麼

上文提到VSCode 有一個特性是跨平台,它的跨平台實質是通過electron 實現的。所以我們需要先簡單了解下electron

官方定義

blank
blank

核心技術

blank
  • 使用Web 技術來編寫UI,用chrome 瀏覽器內核來運行
  • 使用NodeJS 來操作文件系統和發起網絡請求
  • 使用NodeJS C++ Addon 去調用操作系統的native API

應用架構

electronjs.org/docs/tut

blank
  • 1 個主進程:一個Electron App 只會啟動一個主進程,它會運行package.json 的main 字段指定的腳本
  • N 個渲染進程:主進程代碼可以調用Chromium API 創建任意多個web 頁面,而Chromium 本身是多進程架構,每個web 頁面都運行在屬於它自己的渲染進程中

進程間通訊:

  • Render 進程之間的通訊本質上和多個Web 頁面之間通訊沒有差別,可以使用各種瀏覽器能力如localStorage
  • Render 進程與Main 進程之間也可以通過API 互相通訊(ipcRenderer/ipcMain)
点评:普通web 页面无法调用native api,因此缺少一些能力electron 的web 页面所处的Render 进程可以将任务转发至运行在NodeJS 环境的Main 进程,从而实现native API这套架构大大扩展了electron app 相比web app 的能力丰富度但同时又保留了web 快捷流畅的开发体验,再加上web 本身的跨平台优势结合起来让electron 成为性价比非常高的方案

三、VSCode 技術架構

多進程架構

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

開發流程

# 检出代码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

源碼組織

github.com/microsoft/vs

下面是整個VSCode project 的一些頂級的重點文件夾,後文會重點關注src 與extensions:

├── build # 构建脚本├── extensions # 内置插件├── scripts # 工具脚本├── out # 产物目录├── src # 源码目录├── test # 测试代码

VSCode 的代碼架構也是隨著產品階段演進而不斷更迭的:

blank

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

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

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

2. 每層按環境隔離

內核裡面每一層代碼都會遵守electron 規範,按不同環境細分文件夾:

  • common: 公共的js 方法,在哪裡都可以運行的
  • browser: 只使用瀏覽器API 的代碼,可以調用common
  • node: 只使用NodeJS API 的代碼,可以調用common
  • electron-browser: 使用electron 渲染線程和瀏覽器API 的代碼,可以調用common,browser,node
  • electron-main: 使用electron 主線程和NodeJS API 的代碼,可以調用common, node
  • test: 測試代碼
点评:云凤蝶也遇到了类似问题,作为一个低代码+ 可视化的研发平台,许多功能模块的实现都需要横跨编辑态和运行态如果代码不加以限制和区分,很容易导致错误的依赖关系和预期之外的bug因此云凤蝶最终也决定采用(editor/runtime/common) 类似的隔离架构

3. 內核代碼本身也採用擴展機制: Contrib

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

├── backup ├── callHierarchy ├── cli ├── codeActions ├── codeEditor ├── comments ├── configExporter ├── customEditor ├── debug ├── emmet ├──....中间省略无数.... ├── watermark ├── webview └── welcome

Contrib 有一些特點:

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

VSCode 開發團隊做這個設計的目的我猜大概是因為重型的工具軟件功能點實在太多,而且非常多的地方都是採用相似的模式去橫向擴展,如果這些功能代碼直接採用原始的模塊引用的方式在core 裡面硬編碼聚合拼裝起來,是一個自頂向下的架構,對維護性的挑戰比較大。

而採用暴露擴展點的方式,可以將依賴關係反轉,依附於擴展點協議,獨立的小功能的代碼實現可以單獨聚合,核心模塊無需硬編碼和集成所有判斷,整體是一個鬆散式的架構,降低了代碼訊息密度與提升維護性,也更好擴展。

但是VSCode Contrib 的具體業務代碼組織其實看起來沒有太多範式,而且這個內核代碼的擴展機制Contrib 和VSCode 開放給外界的插件化機制extension 是有差異的,讀起來十分頭疼。

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

  • extension 每一個都是運行在歸宿於自己的獨立宿主進程,而contrib 的功能基本是要運行在主進程的
  • extension 只能依附於core 開放的擴展點而活,但是contrib 可以通過依賴注入拿到所有core 內部實現的class (雖然官方不推薦)

4. 依賴注入

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

TS依賴注入常見的實現原理是使用reflect-metadata設置與獲取元訊息,從而可以實現在運行時拿到本來屬於編輯態的TypeScript類型相關元訊息,具體來說就是下面這些API:

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

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

  • Service:服務的實現邏輯
  • Interface:服務的接口描述
  • Client:服務使用方
  • Manger:服務管理器

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

class Client { // 构造函数参数注入(依赖注入方式的一种) constructor( // 必选@IModelService modelService: IModelService, // 可选@optional(IEditorService) editorService: IEditorService ) { // use services } } // 实例化instantiationService.createInstance(Client);

5. 絕對路徑import

blank
blank
点评:绝对路径import 是一个非常值得学习的技巧,具体的方式是配置TypeScript compilerOptions.paths相对路径import 对阅读者的大脑负担高,依赖当前文件位置上下文訊息才能理解假设修改代码的时候移动文件位置,相对路径需要修改本文件的所有import,绝对路径不需要

6. 命令系統

VSCode和monaco-editor都有自己的命令系統,螞蟻CloudIDE團隊的同學也曾經對命令系統的優勢做過總結:

傳統的模塊調用是個網狀,不太好找到一個切面來理解或治理:

而命令系統是中心化的,各功能末端變成了扁平化的結構:

blank

点评:云凤蝶编辑器也遇到这类问题,云凤蝶的自由画布编辑器有非常多的功能,参见《打造媲美Sketch 的自由画布》而且很多情况下一个功能的触发方式是多种多样的,比如大纲树右键菜单触发,顶部工具栏触发,画布右键菜单触发,键盘快捷键触发等这就要求该功能的实现函数能跨区域在多个位置调用,这很容易导致代码依赖关系异常混乱。而命令系统就是一种解决这个问题的很好思路

7. TypeScript

blank
blank
blank

啟動流程(TLDR)

上文初步了解了vscode 的技術架構與源碼組織,手癢的同學估計有點等不及嘗試走一遍vscode 的啟動流程了。

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

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

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

  • 入口:package.json { "main": "./out/main" } : electron 的標準啟動入口
  • '/out/main.js': 是構建產物的入口文件,它對應源碼'/src/main.js'
// /src/main.js 的精簡核心鏈路const { app, protocol } = require('electron'); app.once('ready', function () { // electron 啟動好之後,調用vscode 的入口onReady(); }); async function onReady() { // 獲取緩存文件目錄地址和語言配置,執行啟動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(); // 執行主入口函數code.main(); class CodeMain { main() { // vscode 的class public 入口一般只是空殼,真正的都在private邏輯裡面this.startUp(); } private async startup() { // 先創建依賴的初始化service const [instantiationService, instanceEnvironment] = this.createServices(); // 創建編輯器實例並調用startUp 方法return instantiationService.createInstance (CodeApplication).startup(); } } // /src/vs/code/el ectron-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); // 創建一大堆依賴的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() { // 執行open this.doOpen(); } private doOpen() { // 打開瀏覽器窗口this.openInBrowserWindow(); } private openInBrowserWindow() { // 創建窗口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() { //調用electr on 的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))}`; } }

代碼編輯器技術

因為本文關注的重點並不在真正的代碼編輯器技術而是在調研一下大型軟件的工程化,因此本文只會簡要介紹一下代碼編輯相關的的一些核心技術:

  • monaco-editor 文本編輯器,非常精深的領域,不展開聊,可以看一些有意思的細節:
  • language server protocol 語言提示, 也是vscode 的一大創舉:
    • 不再關注AST 和Parser,轉而關注Document 和Position,從而實現語言無關。
    • 將語言提示變成CS 架構,核心抽象成當點擊了文檔的第幾行第幾列位置需要server 作出什麼響應的一個簡單模型,基於JSON RPC 協議傳輸,每個語言都可以基於協議實現通用後端
blank
blank
blank
blank
blank
点评: monaco-editor 可以看出专家级人物的领域积累,属于VSCode 的核心竞争力language server protocol 和Debug Adaptor Prototal 的设计就属于高屋建瓴可以看出思想层次,把自己的东西做成标准,做出生态,开放共赢

四、VSCode 插件系統

理念差異

blank

對比幾大IDE:

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

VSCode 插件的強隔離

blank
  • 獨立進程:VSCode plugin 代碼運行在只屬於自己的獨立Extension Host 宿主進程裡
  • 邏輯與視圖隔離:插件完全無法訪問DOM 以及操作UI,插件只能響應VSCode Core 暴露的擴展點
  • 視圖擴展能力非常弱:VSCode 有非常穩定的交互與視覺設計,提供給插件的UI 上的洞(component slot)非常少且穩定
  • 只能使用限制的組件來擴展:VSCode 對視圖擴展的能力限制非常強,洞裡面的UI 是並不能隨意繪製的,只能使用一些官方提供的內置組件,比如TreeView 之類

vscode 有哪些擴展能力? code.visualstudio.com/a

点评:视图扩展的克制带来统一的视觉与交互风格,带来好的用户体验,便于建立稳定的用户心智插件独立进程,与视图隔离,保证整体软件的质量、性能、安全性

Workbench 視圖結構

code.visualstudio.com/d

blank
  • 標題欄: Title Bar
  • 活動欄: Activity Bar
  • 側邊欄: Side Bar
  • 面板: Panal
  • 編輯器: Editor
  • 狀態欄: Status Bar

在這個視圖結構裡面有哪些可擴展呢?詳見extending workbench

blank

插件API 注入

插件開發者調用core 能力時需要引入名為vscode 的npm 模塊

import * as vscode from 'vscode';

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

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

// /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注入到了運行環境。

插件開發與配置

腳手架: github.com/Microsoft/vs
官方demo: github.com/Microsoft/vs

npm install -g yo generator-code yo code

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

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

一些關鍵配置如下:

main:主文件入口,比如導出一個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 激活時機

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

Contribution Points 擴展點

  • contributes.configuration:本插件有哪些可供用戶配置的選項
  • contributes.configurationDefaults:覆蓋vscode 默認的一些編輯器配置
  • contributes.commands:向vscode 的命令系統註冊一些可供用戶調用的命令
  • contributes.menus:擴展菜單
  • ...

五、感想

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

  • 健壯的結構遠比精巧的設計來得重要。換句話說,結構是第一位的,功能是第二位的。
  • 保持項目的簡單性。設計達到完美的時候,不是無法再增加東西了,而是無法再減少東西了。

VSCode 的一些工程上的優秀設計,比如依賴注入、絕對路徑引用、命令系統對於雲鳳蝶來說是可以馬上學以致用的,而contrib 與extension 的擴展系統,則非一日之功,也並不宜盲目下手。

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

  • 用戶核心在操作的資源是什麼?
  • 用戶的關鍵路徑是什麼?
  • 這個軟件的整體功能形態,交互與視覺設計已經穩定了嗎?
  • 內核功能區和第三方擴展的功能域之間的界限在哪裡?
  • 哪些環節可能會出現外溢需求需要第三方擴展才能被滿足,不適宜官方動手做嗎?

對VSCode 而言:

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

對雲鳳蝶而言:

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

圍繞這個思考,雲鳳蝶將持續吸納優秀的思想與架構,持續將編輯器的核心功能鏈路打磨通透,底層架構搭建穩定。

六、相關資料


未來已來,時不我待!
雲鳳蝶招聘前端、Java、PD、設計崗位,未來等你共創!
如果你感興趣,歡迎聯繫[email protected][email protected]

What do you think?

Written by marketer

blank

微前端的核心價值

blank

SEE Conf 大會邀請函| 體驗美好,玩轉數據