核心包仅325KB、重依赖按需拉取:DSH-better-sidebar性能优化背后的完整原理
【免费下载链接】DSH-better-sidebar开放的侧边栏底座,支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 | Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar
DSH-better-sidebar 是一个开放的侧边栏底座:支持三方扩展注册新的侧边栏页面,并内置文件渲染编辑、终端、侧边对话、Git 与子代理页面。它最亮眼的性能设计是——浏览器启动时只下载约 325KB 的核心包,而编辑器、终端、Mermaid 图表这类重依赖全部按需拉取。这篇文章带你完整拆解这套性能优化的背后原理。
1️⃣ 问题背景:为什么启动会这么重
拆分之前,客户端是一个单文件 bundle(约 24.6MB),页面一启动就要全部下载 + 解析 + 执行。体积大头来自预览类重依赖:
| 重依赖 | 体积 | 用途 |
|---|---|---|
@univerjs/*全家桶(含 echarts/zrender) | ~18MB | .xlsx 预览 |
@codemirror/*+@lezer/* | ~1.9MB | 文本编辑器高亮 |
mermaid+ 图论引擎 | ~7MB | Markdown 图表渲染 |
xterm | ~0.3MB | 终端 |
| 插件自身代码 | ~0.35MB | — |
问题在于:绝大多数用户启动时根本不会打开 .xlsx 或 Mermaid 图表,却要为它们付出下载与解析成本。即使用动态import(),也只是「延迟执行」——下载成本依然发生在启动时。
优化目标很明确:重依赖拆成独立脚本,首次打开对应功能才下载;启动只拉核心(~325KB)。
2️⃣ 原理一:懒加载分块(Lazy Chunks)——用到才付钱
拆分的核心思路是把每个重功能构建成独立脚本,按需注入。整个机制分构建期和运行期两段:
构建期:每个 chunk 由 tsdown.config.ts 编译成独立的浏览器 bundle(lib/client-<name>.js),脚本首行会把自己注册到页面级全局注册表globalThis.__dshChunks__中,而不是走宿主的模块加载器——这样解析行为不依赖具体宿主版本,跨版本最稳。
运行期:chunk-loader.ts 的loadChunk负责三步物化:
- 注入
<script src="/sidebar/bundle/<name>.js">,由插件自己的路由 bundle-route.ts 服务; - 从全局注册表读取该 chunk 的 factory;
- 用自定义
require物化——外部依赖(react、cordis 等)经宿主模块表解析,命中「seed 分支」这条跨版本一致的稳定路径。
当前的分块清单(完整设计见 lazy-chunks 设计文档):
| Chunk | 构建产物 | 触发时机 |
|---|---|---|
| 核心 | client.js(~325KB) | 页面启动 |
| editor | client-editor.js(~1.6MB) | 打开 code / md / html 文件 |
| terminal | client-terminal.js(~450KB) | 打开终端 tab |
| mermaid | client-mermaid.js(~7MB) | 预览含 mermaid 的 md |
| locale | client-locale.js | 安装了 better-locale 插件时才拉 |
体验细节同样讲究:lazy-chunk.tsx 给每个懒组件包了一层「loading / error / retry」三态视图——某个 chunk 加载失败只会让该视图显示错误与重试按钮,侧边栏其余部分照常工作。
3️⃣ 原理二:三层缓存契约——按需拉取不等于反复下载
「按需」意味着可能被多次触发,项目为此设计了三层缓存,每层都带失败兜底:
| 层 | 机制 | 失败路径 |
|---|---|---|
| 内存 | 每 chunk 一个 in-flight promise,并发去重 | 失败删除缓存项,下次重试 |
| 执行 | 脚本执行即覆盖全局注册表槽位(赋值幂等,无重复注册错误) | 物化失败清缓存,重新注入脚本 |
| HTTP | no-cache+ ETag,If-None-Match命中返回 304 | 文件变化 ETag 轮转,返回 200 |
HTTP 层的价值最大:页面刷新、热更新重新激活时,未变化的多 MB chunk 直接 304 免重下载——刷新一次页面不用把 7MB 的 mermaid 再拉一遍。
4️⃣ 原理三:运行时开销瘦身——从轮询到渲染
体积只是性能的一半,运行时 CPU 成本同样要抠。性能优化设计文档 列了 10 项改动,挑几个代表性的:
- 启动 RPC 合并:boot 阶段两次串行
settings.get合并为一次,并给第二个请求补上 2s 超时兜底(此前 RPC 挂起会导致侧边栏永不出现); - Git 轮询降频:GitLens 的 2s 轮询不再每 tick 重列 worktrees,git 进程 spawn 每分钟 60 个降到约 34 个(-43%);
- 渲染稳定化:SideChat 转录中内容未变的行复用旧对象,让下游 React 调和与 markdown 解析整段跳过;FileTree 的展开集合由数组
includes()线性扫描改为 Set; - 编辑器不再每键全文字符串化:从每次按键 O(文档长度) 的
doc.toString()降为 0(只翻 dirty 标记)。
真机实测收益(Playwright 无头 Chromium,每指标 3 次取中位数,测量 lane 见 tests/e2e/perf.e2e.ts):
| 指标 | 优化前 | 优化后 | 变化 |
|---|---|---|---|
| 挂载时延(navigationStart → host attach) | 376.9 ms | 272.9 ms | -28% |
| 宿主聚合 bundle 传输量(gzip) | 1,436 KB | 1,234 KB | -203 KB / -14% |
| 核心包体积 | 基准 | 再瘦身 45% | 词典等 ~640KB 源码移出核心 |
5️⃣ 快速上手:三步复现
git clone https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar cd DSH-better-sidebar pnpm install构建产物会生成核心lib/client.js与一组lib/client-<name>.js按需 chunk。可以用 scripts/e2e-mount.sh 把构建好的插件挂载到 DSH 宿主跑冒烟,性能回归则由 perf 测量 lane 与 tests/lazy-chunk.spec.tsx、tests/chunk-artifact.spec.ts 等契约测试长期钉住。
6️⃣ 总结:三句话记住这套性能优化原理
- 先拆再按需:重依赖在功能首次打开时才下载、解析、执行,启动路径只保留 325KB 核心包;
- 三层缓存保底:内存去重 + 幂等执行 + HTTP ETag 304,保证「按需拉取」永远不会变成「反复拉取」;
- 运行时开销也是性能:轮询频率、渲染复用、启动 RPC——一堆小改动叠加出挂载时延近三成的提升。
【免费下载链接】DSH-better-sidebar开放的侧边栏底座,支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 | Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考