十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VSCode扩展升级为Electron桌面打字游戏:架构改造与踩坑实录

VSCode扩展升级为Electron桌面打字游戏:架构改造与踩坑实录 做 VSCode 打字扩展做到第三版时我萌生了一个想法把它抽出来做成一个真正的桌面打字游戏应用。VSCode 里的 Webview 扩展虽然方便但受限于编辑器窗口和扩展 API 的边界做全屏专注模式、自定义快捷键、系统级菜单这些事总觉得隔了一层。于是我用 Electron Vue 3 做了一次完整的架构改造把原来挂在编辑器里的扩展逻辑重构成一个独立分发的桌面应用。这篇文章就是这次改造的完整复盘包含技术选型、架构分层、核心引擎封装、打包分发和踩坑记录适合正在做 Electron 应用、或者想把 VSCode 扩展升级成独立产品的开发者参考。1. 改造动机为什么把 VSCode 扩展拆成独立应用1.1 扩展形态的三个痛点很多人问过我VSCode 扩展用 Webview 就能跑 HTML JS打字游戏这种形态完全能在里面实现为什么还要折腾成 Electron我实际做了大半年扩展后感受最深的是三个限制。第一个痛点是性能隔离。VSCode 扩展的 Webview 运行在编辑器渲染进程中虽然有独立的 webview 标签页但和编辑器的 DOM、插件消息、主题切换等机制共享进程资源。打字游戏要频繁处理键盘事件、实时渲染文本、维护波形和统计在高分屏或者大文件打开的情况下明显能感到按键响应延迟。有一次用户反馈说某些场景下按键有 30ms 左右的延迟虽然不太起眼但对打字游戏这种毫秒级体验敏感的应用这就是硬伤。第二个痛点是交互边界。扩展里想做一个全屏无干扰模式只能通过 Webview 全屏 隐藏编辑器 UI效果勉强但总有瑕疵窗口拖拽、标题栏控制、多显示器支持都受 VSCode 窗口体系约束。想监听快捷键还得注册 keybinding 并处理与编辑器原有快捷键的冲突体验很难做好。第三个痛点是分发门槛。扩展要通过 VSCode 插件市场审核对游戏类应用来说更新节奏和渠道控制都不够自由。而且用户必须安装 VSCode 才能用你的应用这个前置条件本身就过滤掉了很多只是想练打字的人。1.2 独立应用的核心收益改成独立 Electron 应用之后这几个痛点基本都解决了。渲染进程只跑游戏本身不用跟编辑器抢资源启动后内存占用反而比 VSCode 扩展更低。窗口完全自控我可以自由做无边框窗口、开机自启、全局快捷键、系统托盘这些都是扩展形态很难做到的。分发逻辑也彻底变了。Electron 应用可以打包成 exe、dmg、deb、AppImage用户下载双击即可运行不需要任何前置环境。而且我可以自己控制更新节奏不需要等插件市场审核。像卡顿优化、新关卡、词库更新这些功能都可以及时推到用户手里。还有一点容易被忽略独立应用让用户觉得这是一个“产品”而不是一个编辑器里的“小玩具”。这对鼓励持续使用、建立用户习惯是有实际帮助的尤其打字练习这种需要长期坚持的场景产品感很重要。1.3 一个判断原则什么时候该拆、什么时候不该拆如果你也在纠结要不要把 VSCode 扩展拆成独立应用我建议先做一次成本收益判断。扩展形态适合工具属性强、和编辑器工作流强绑定的场景比如格式化、代码片段、文档预览、linter 提示。这些应用离了编辑器就没意义拆出去反而是倒退。但如果你做的东西本质上是个独立产品编辑器只是个临时的运行壳比如游戏、教程工具、本地知识库、写作软件那拆出来只是时间问题。判断标准很简单用户能不能在不打开编辑器的情况下理解并使用你的功能如果能就应该考虑拆了。当时我列了个表把扩展能做的、Electron 能做的都写下来发现键盘事件控制、窗口设计、多渠道分发这三项扩展是明显跟不上的所以拆的决定很明确。2. 整体架构设计Electron Vue 3 的分层逻辑2.1 为什么是 Electron 而不是 Tauri 或原生技术选型的时候我认真对比过 Tauri也考虑过用 Rust egui 写原生但最后还是选了 Electron。原因倒不是情怀而是结合项目现状做的选择。原来的扩展就是 HTML TypeScript Vue整个打字游戏的 UI、动画、词库渲染逻辑都已经在 Webview 里跑通了。拆成 Electron 后渲染进程可以直接复用这套 Vue 组件和样式工作量大幅压缩。如果换 Tauri虽然包体积更小、内存更优但前端嵌入方案是 WebView在国产 Linux 系统上容易出现 WebView 环境不一致的问题而且 Rust 侧的插件生态我还不熟时间成本不可控。Electron 内置 Chromium不管什么系统渲染行为是确定的这对工具类应用很重要——我不希望同一套 Vue 代码在某台机器上布局就变了。另外Electron 的成熟生态也是加分项。electron-builder 打安装包、electron-updater 做增量更新、主进程 API 查文档就能用这些都是在社区里被反复验证过的方案。省下来的时间可以专注于打字游戏本身。2.2 主进程 / 渲染进程 / 预加载脚本如何分工这次架构改造的核心是把原来 VSCode 扩展里混合在一起的逻辑拆成三层。主进程负责系统级能力创建窗口、自定义菜单、监听全局快捷键、读写用户数据文件、获取系统语言、处理单实例锁。渲染进程只做 UI 渲染和打字游戏状态机不直接碰操作系统 API。预加载脚本 contextBridge 暴露一套白名单 API渲染进程通过window.api调用主进程能力本质上建立起了一条清晰的 IPC 通道。这里最关键的是隔离原则渲染进程永远不应该直接使用 Node.js API。Electron 出于安全原因默认开启了 contextIsolation意思就是渲染进程拿不到 Node 的require、process等能力。很多人图省事会在渲染进程直接require(electron)或者nodeIntegration: true这等于把系统权限暴露给了前端代码一旦某个第三方依赖有漏洞攻击面就是整个用户系统。我的做法是严格使用contextBridge.exposeInMainWorld只把需要的方法暴露出来比如window.api.readRecords()、window.api.saveRecord()这些。2.3 复用还是重写从 VSCode Webview 迁移的取舍拆架构时最纠结的问题是UI 层能不能直接复制实际上VSCode Webview 和 Electron 渲染进程虽然都跑 HTML JS但 API 差异不小。VSCode Webview 里常用的acquireVsCodeApi().postMessage()这套消息机制到 Electron 这边要换成ipcRenderer.invoke()或者ipcRenderer.send()。获取数据的方式也不同VSCode 扩展用context.globalState存用户数据Electron 得自己处理用户目录下的 JSON 文件。我在迁移时做了个很实际的决策UI 组件和样式 100% 复用所有数据访问与系统调用全部重写。具体的做法是给渲染进程定义一个接口层。原来组件里直接调用vscode.postMessage()的地方全部改成调用window.api.getLessonList()、window.api.saveBestRecords()这类业务方法。这样以后哪怕再换到 Tauri只需要替换window.api的实现UI 完全不用动。这种面向接口而非面向 API 的写法是这次架构改造里我做的最正确的决定之一。2.4 项目目录结构改造后的项目结构长这样清晰隔离了主进程、预加载脚本和渲染进程。typing-game/ ├── electron/ │ ├── main.ts # 主进程入口 │ ├── preload.ts # 预加载脚本暴露 window.api │ ├── menu.ts # 自定义菜单 │ ├── storage.ts # 用户数据读写 │ └── locale.ts # 系统语言检测 ├── src/ │ ├── components/ # Vue 组件 │ ├── views/ # 页面级组件 │ ├── composables/ │ │ ├── useTypeEngine.ts # 打字引擎 │ │ ├── useStats.ts # 统计逻辑 │ │ └── useAudio.ts # 音效管理 │ ├── api/ # 渲染进程调主进程的统一入口 │ └── App.vue ├── packages/ # 打包配置 └── electron-builder.yml这样分层之后前端同学可以从src目录入手不需要理解 Electron 细节负责打包和系统适配的人只改electron目录。职责边界非常清楚。3. 打字游戏核心引擎实操从 Vue 组件逻辑到可复用引擎3.1 useTypeEngine用组合式 API 封装打字引擎原扩展里所有打字逻辑都写在TypingPanel.vue组件内部状态和 UI 混在一起改一个功能容易碰坏另一块。这次改造我选了 Vue 3 的组合式 API把打字引擎彻底抽成了一个独立的useTypeEngine组合函数。先说为什么用组合式 API 而不是选项式。打字游戏的状态非常多当前文本、当前索引、错误次数、开始时间、暂停状态、完成时间、速度波动、准确率。这些状态之间还有复杂的联动关系选项式 API 中 data、computed、methods 分开写状态多起来之后维护成本极高。组合式 API 允许我把“打字引擎”这个完整逻辑域的所有数据和方法封装到一个函数里把“统计”“词库切换”“音效”分别拆到独立组合函数再按需组合。这其实就是模块化思维在组件内的延伸。useTypeEngine的核心实现大概是这样的export function useTypeEngine() { const text ref() const index ref(0) const errorCount ref(0) const status refidle | running | paused | finished(idle) let startTime 0 let endTime 0 function start(newText: string) { text.value newText index.value 0 errorCount.value 0 status.value running startTime performance.now() } function handleKeydown(e: KeyboardEvent) { // 跳过修饰键和组合键 if (e.ctrlKey || e.metaKey || e.altKey) return // 输入法组合期间不判定 if (e.isComposing || e.keyCode 229) return if (status.value ! running) return const expected text.value[index.value] const typed e.key if (typed expected) { index.value if (index.value text.value.length) { status.value finished endTime performance.now() } } else { errorCount.value } } function speed() { const minutes (endTime - startTime) / 60000 return minutes 0 ? Math.round(index.value / 60 / minutes) : 0 } return { text, index, errorCount, status, start, handleKeydown, speed } }e.isComposing这个判断特别重要中文输入法在按下字母键时会先进入 composition 状态准备组词这时候keydown事件会被触发但keyCode是 229如果不跳过中文输入过程中按的字母会被误判为打字输入统计直接爆炸。这个坑我后面会详细说。3.2 打字判定的细节大小写、修饰键和输入法干扰打字游戏的核心判定逻辑坑非常多这里展开讲三个我花了很久才理顺的点。第一个是大小写问题。如果文本是小写字母用户按 Shift 键会输入大写而e.key返回的是实际输入的字符。所以单纯拿e.key expected做判断大写状态全部会判错。我的处理方式是统一用e.key.toLowerCase()和text[index].toLowerCase()比较视觉上只检查用户是否按对了字符不检查 Shift 是否误开。当然游戏如果设计成必须区分大小写模式那就要单独处理但在默认的英文打字练习场景忽略大小写是更友好的做法。第二个是修饰键和快捷键。按下 Shift、Ctrl、Alt、Meta 等修饰键时e.key分别是Shift、Control这些如果不跳过游戏会把这些当成错误输入错误率直接飘红。我加了一个判断if ([Shift, Control, Alt, Meta].includes(e.key)) return。还有一个隐藏坑——当用户按了 Tab 或方向键它们也会触发keydown但这些不应该是打字输入也要过滤掉。第三个是输入法干扰。这个问题在无边框窗口和全局模式下尤其明显。如果系统默认输入法是中文用户按字母键时Electron 窗口会响应用户的输入法状态导致 keydown 事件进入 composition 流程。最彻底的方案是给窗口设置禁用输入法mainWindow.webContents.setIgnoreMenuShortcuts(true)但更直接的办法是监听组合事件。我最终做的是在keydown里判断e.isComposing同时在compositionstart时把当前状态标记为“输入法激活”直到compositionend才恢复接收按键。双保险之后中文输入法的干扰就彻底消失了。3.3 统计逻辑速度、准确率与完成度打字游戏的统计指标有三个速度WPM/CPM、准确率、完成度。这三个指标看起来简单但计算口径稍不注意就会自相矛盾。速度的通用做法是记录开始时间结束时用字符数 / 用时秒 * 60得到 CPM再除以 5 得到 WPM。这里有个我自己定的小规则即使打错了字符也算打过的字符所以速度统计依据的是“击键通过的位置”而不是“正确字符数”否则准确率和速度会互相干扰用户会看到速度低且准确率也低体验很挫败。更合理的口径是速度看你出手有多快准确率看你判断有多准两个维度独立统计。用 TypeScript 实现统计函数export function calcStats(index: number, errorCount: number, elapsedMs: number) { const minutes elapsedMs / 60000 const cpm minutes 0 ? Math.round(index / minutes) : 0 const wpm Math.round(cpm / 5) const totalTyped index errorCount const accuracy totalTyped 0 ? Math.round((index / totalTyped) * 100) : 100 return { cpm, wpm, accuracy } }这里要特别强调elapsedMs的采集方式一定用performance.now()不要用Date.now()。因为performance.now()是相对页面加载零点的高精度时间戳不受系统时间调整影响而且精度到微秒级。曾经一次系统自动校时导致Date.now()往回跳用户的每局成绩全部变成了负数查了半天才定位到这个问题。完成度更简单index / text.length。但 UI 上我同时展示了一个进度条和一个百分比数字进度条的动画用 CSS transition避免 setInterval 频繁刷新 DOM 导致掉帧。4. Electron 主进程与系统集成改造4.1 自定义菜单替换默认菜单Electron 默认的应用菜单是一套通用模板对打字游戏来说完全没意义用户右键还能看到“重新加载”“开发者工具”这种选项既不美观也不安全。我自定义了菜单只保留游戏需要的操作。const template: MenuItemConstructorOptions[] [ { label: 游戏, submenu: [ { label: 重新开始, accelerator: CmdOrCtrlR, click: () mainWindow?.webContents.send(game:restart) }, { label: 暂停 / 继续, accelerator: Space, click: () mainWindow?.webContents.send(game:toggle-pause) }, { type: separator }, { label: 退出, role: quit } ] }, { label: 视图, submenu: [ { label: 切换深色模式, click: () mainWindow?.webContents.send(theme:toggle) }, { type: separator }, { label: 全屏, role: togglefullscreen } ] }, { label: 帮助, submenu: [ { label: 项目主页, click: () shell.openExternal(https://example.com) } ] } ] Menu.setApplicationMenu(Menu.buildFromTemplate(template))自定义菜单的作用不只是美学它能克制 Electron 默认快捷键带来的副作用。比如默认的CmdOrCtrlR是 reload 窗口对打字到一半的用户来说是毁灭性打击所以我重新定义了它。注意这里我用了webContents.send给渲染进程发消息而不是直接用 reload这样游戏可以自己决定重开一局的逻辑而不是粗暴刷新页面。4.2 获取系统语言与国际化方案说到菜单就绕不开多语言。原扩展只支持英文拆成独立应用后我觉得至少要把中英文切换做了这时候就需要读系统语言来决定初始语言。Electron 主进程里获取系统语言有两种做法// 推荐应用当前语言受 app 命令行设置影响 const locale app.getLocale() // Electron 24获取系统区域设置更接近 OS 层 const systemLocale app.getSystemLocale()app.getLocale()会返回zh-CN、en-US这种格式的字符串。但我发现一个细节macOS 和 Windows 返回的格式不完全一致Windows 上有时是zh-CNmacOS 上可能是zh-Hans。所以解析的时候不能直接startsWith(zh)我选择做了个小映射表兼容常见变体。这里要注意的是菜单构建时要传当前语言然后通过Menu.setApplicationMenu重新设置一遍。更完整的方案是引入 i18n 库把渲染进程的文案统一管理。我用 Vue 的useI18n()配合主进程传过来的 locale切换语言时通过 IPC 同步窗口标题和菜单。4.3 窗口控制与外部链接处理独立应用有一个逃不掉的问题渲染进程里有很多链接需要打开。比如“帮助”菜单里的项目主页或者游戏内点击评分记录里的 GitHub 链接。如果直接让渲染进程window.open(url)默认会在 Electron 窗口里开一个新窗口而不是用户的系统浏览器体验很怪。正确的做法是用setWindowOpenHandler拦截所有新窗口请求把外链交给系统默认浏览器同时阻止 Electron 内部打开mainWindow.webContents.setWindowOpenHandler(({ url }) { if (url.startsWith(https://) || url.startsWith(mailto:)) { shell.openExternal(url) } return { action: deny } })这里有个安全细节一定要校验 url 协议只允许 https 或 mailto禁止 file:// 协议避免恶意链接读取本地文件。另外渲染进程里如果有些内嵌窗格需要保留在应用内部比如某个基于 WebView 的预览页面可以单独用一个BrowserView配合白名单协议管理这个来自热搜里有人问的 “electron 壳子内的页面打开 url” 的问题——本质就是区分哪些链接交给内部展示、哪些交给系统浏览器不要混在一起。4.4 数据持久化从扩展存储迁移到本地文件VSCode 扩展时代用context.globalState存用户设置和最高纪录迁移到 Electron 后需要自己的存储方案。我的选择是简单数据用 JSON 文件复杂数据不用数据库因为写字游戏的数据量并不大记录量到几万条都很小。引入数据库反而要处理原生模块编译问题纯 JS 的存储库在 Electron 里不香。我的实现是写一个storage.ts数据文件放app.getPath(userData)目录下的records.json。import { app } from electron import { join } from path import { readFileSync, writeFileSync } from fs const dataFile join(app.getPath(userData), records.json) export function readRecords(): Record[] { try { return JSON.parse(readFileSync(dataFile, utf-8)) } catch { return [] } } export function saveRecord(record: Record) { const records readRecords() records.push(record) // 只保留最近 1000 条防止文件无限膨胀 const trimmed records.slice(-1000) writeFileSync(dataFile, JSON.stringify(trimmed), utf-8) }写入要特别注意写文件时机最好用防抖延迟落盘避免打字结束时频繁写盘。我在游戏结束时批量写入一场比赛的所有分段记录而不是每打一个字符存一次这个性能差距在机械硬盘上非常明显。5. 打包与跨平台分发从“我在我电脑上能跑”到“谁都能装”5.1 electron-builder 基础配置与多平台目标打包是我花时间第二多的地方坑密度极高。我用的是 electron-builder配置文件electron-builder.yml核心段落如下appId: com.example.typinggame productName: TypingGame directories: output: release files: - dist/**/* - electron/** - package.json asar: true win: target: - nsis icon: build/icon.ico nsis: oneClick: false allowToChangeInstallationDirectory: true mac: target: - dmg category: public.app-category.games linux: target: - AppImage - deb category: Game这个配置本身不复杂但我踩了一个大坑electron/**目录必须确保打包进去之后主进程入口路径正确。如果 package.json 里的main指向electron/main.js那files里就要包含electron目录。曾经有一次我把electron目录改成dist-electron忘了同步 package.json 的main字段结果所有平台打包出来的应用双击就闪退控制台报错找不到模块排查花了一晚上。打包后的产物包含一个win-unpacked目录和一个 NSIS 安装包。Windows 上还碰到了一个很现实的问题没有代码签名时SmartScreen 会弹“Windows 已保护你的电脑”。对于国内个人开发者来说买 EV 证书太贵普通 OV 证书也开销不小。短期的过渡方案是引导用户在安装时点“更多信息 → 仍要运行”但长期看如果需要面向大众分发代码签名终究绕不过去这是 Windows 生态的硬规则。5.2 国产系统银河麒麟等的分发注意点网上很多人在搜“electron 国产系统分发”“银河麒麟 electron 版本”这确实值得单独聊聊。国产系统上 Electron 应用主要遇到两个问题高版本 Electron 依赖的 glibc 版本可能高于系统自带以及缺系统依赖库。我实际在银河麒麟 V10 上测过Electron 26 默认打包的 deb 包安装可以成功但运行时提示缺少 libnss3、libatk-bridge2.0 等动态库。解决办法有两种一种是把这些依赖打进 deb 包的Depends里让系统自动安装另一种是直接用 AppImage它自带大部分依赖兼容性更好。但 AppImage 也有问题部分国产系统默认没有libfuse2AppImage 跑不起来。所以我的建议是发布 deb 包走软件包管理器安装同时提供 AppImage 作为替代并把依赖说明写清楚。另外国产系统上的窗口边框渲染、字体渲染与 Windows/macOS 差异较大尤其是高分屏缩放需要在主进程主动调用app.commandLine.appendSwitch(high-dpi-support, 1)否则界面会发虚或控件巨大。5.3 包体优化与常见打包报错Electron 应用包体大是众所周知的痛点动辄 80MB。打字游戏这种工具类小应用用户很难接受下载一个超大安装包。我做了三件事压缩体积。第一是开启 asar 归档把所有业务代码打进一个 asar 包文件数量少了加载速度也快一些。第二是裁剪依赖。打包时 electron-builder 会把node_modules里 production 依赖都打进去但如果某些依赖只是开发用的比如 typescript、vite就应该确保它们在 devDependencies 里electron-builder 不会打包开发依赖。第三是关闭不必要的自动更新模块——electron-updater 如果不配置镜像源国内网络下每次启动都会静默请求 GitHub release既慢又笨纯单机游戏暂时不需要它。常见的打包报错里最经典的是“After pack: cannot find module electron-updater”或“cant resolve fs in renderer process”。这两个本质上都是因为把 Node 核心模块或主进程依赖错误地引入到了渲染进程。我的排查心得是渲染进程代码里严禁 importfs、path、os等 Node 模块如果第三方库依赖 Node 模块就不要在渲染进程使用它而是通过 IPC 放到主进程调用。这条铁律贯彻好一半的打包报错都不会出现。6. 上线后的踩坑记录与优化方向6.1 高价值踩坑实录速查表改造上线后收集了不少用户反馈也踩了不少坑我把最有价值的几条整理成速查表方便后来人直接查。现象根因解决办法中文输入法下按字母键被误判为打字keydown 进入 composition 状态keyCode 229判断 e.isComposing / keyCode 229配合 compositionstart/endCtrlR 或 Space 被系统菜单占用游戏逻辑收不到按键Electron 默认应用菜单抢占了快捷键自定义 Menu 模板或监听 before-input-event 拦截打包后在非开发机启动白屏控制台报 file:// 资源 404渲染进程入口路径写死相对路径打包后目录结构不同一律用path.join(__dirname, ../dist/index.html)定位入口文件Linux 下 deb 安装运行提示缺 .so 文件electron 依赖系统库未声明在 deb 的 Depends 字段声明 libnss3、libatk 等依赖或改用 AppImage游戏跑久了内存缓慢上涨WebContents 里积累了大量的 IPC 监听器、事件回调没有 removeListener组件卸载时清空监听IPC 回调用off()注销高 DPI 缩放下界面文字模糊未开启 high-dpi-support或未处理 devicePixelRatio主进程开启高分屏支持CSS 中使用 rem/vw 适配6.2 性能优化从 60 FPS 到更顺滑打字游戏的渲染层优化有自己的一套逻辑。早期版本每个按键更新一个 Vue 响应式数组通过 v-for 渲染几十个字符实际上 60 帧完全没问题。但在长篇文章模式下单次渲染的 DOM 节点会超过 2000 个Vue 的响应式更新就会开始掉帧。我的优化思路是分片渲染只渲染当前视口附近的内容用一个固定高度的容器加虚拟滚动。具体实现不复杂维护一个visibleRange根据滚动位置算出该显示哪 50 个字符节点其他的字符统计逻辑在 JS 里计算不渲染成 DOM。这样长文本模式下 DOM 节点始终稳定在 100 个以内FPS 稳定在 144 Hz 显示器上也不掉帧。另一个性能点是打字时的音效。如果每次按键都同步创建一个Audio对象并play()高频率下会有延迟。我改成预加载 35 个短音效 buffer在AudioContext里用一个播放队列轮询选择空闲的 buffer 来播放这样按键音不再卡顿也不会因为连打触顶限制而漏音。6.3 后续扩展方向路由、远程内容与数据同步现在的版本是纯本地的单窗口应用没有使用 Vue Router。因为游戏就几个页面主菜单、游戏页、统计页、设置页用组件切换就够了。但如果后面要加入选关系统、多模式教学、用户系统建议引入 Vue Router但要注意 Hash 模式因为 Electron 生产环境使用 file:// 协议加载入口文件History 模式在深链刷新时会 404。这是很多从 Web 转向 Electron 的开发者会踩的坑。另外热搜里有人问“vue 播放 m3u8”这个在打字游戏里暂时用不上但如果是做带背景音的教学游戏可以考虑在渲染进程里用 hls.js 播放音视频流。数据的未来方向是云同步把个人最佳成绩、词库进度同步到自己的服务器——参考 springboot vue 前后端分离的思路客户端只负责数据上报和展示服务端管账号和成绩排行。这是独立应用走向产品化很自然的一步但要考虑隐私设计尽量匿名化上报。如果你也想把一个 VSCode 扩展拆成独立应用我的建议是先花半天时间把所有用到 VSCode API 的地方列出来逐个对照 Electron 的能力找替代方案再动手写代码。架构改造的价值不在于“换了壳”而在于你把逻辑重新梳理清楚让它脱离宿主环境也能独立生存。整个改造过程会遇到的坑很多但每解决一个你对 Electron 的理解就更深一层——尤其是主进程和渲染进程的边界感、IPC 消息的设计、打包分发这些和扩展开发完全不同的维度。这套改造跑通之后下一款应用的上手速度会快非常多。
返回列表