Skip to main content

构建优化

Elftia 主 bundle 已从 1,812 KB 优化至 759 KB (-58%)。本文档记录优化策略和规范,防止退化。


Bundle 大小限制

硬性限制(必须遵守)

指标限制当前值检查方式
最大单个 chunk< 1 MB759 KBnpm run verify:build
主 bundle (gzipped)< 300 KB221 KBnpm run verify:build
Vite 警告0 个0 个构建输出

推荐目标

指标目标当前值优先级
主 bundle (未压缩)< 500 KB759 KBP1
总 bundle< 3 MB6.24 MBP2
首屏加载< 2s~2sP0

检查命令

{/* 快速验证(4 项关键指标) */}
npm run verify:build

{/* 详细分析(chunk 分类统计) */}
npm run analyze:build

{/* 可视化分析(生成 stats.html) */}
npm run build:renderer
{/* 打开 dist/stats.html */}

代码分割策略

路由级代码分割(必须)

所有页面组件必须使用 React.lazy 懒加载。lazy/ 是按用途拆分的目录(pages.tsx / workspaces.tsx / inline.tsx / shell.tsx / skeletons.tsx / with-suspense.tsx / index.ts barrel),加新 page 改 lazy/pages.tsx

{/* packages/renderer/src/app/lazy/pages.tsx */}
export const LazySettingsPage = lazy(() => import('../Settings'));
export const SuspenseSettings = withSuspense(LazySettingsPage, PageSkeleton);

{/* packages/renderer/src/app/App.tsx — 旧 import 路径仍然有效(barrel re-export) */}
import { SuspenseSettings as Settings } from './components/lazy';
<Route path="/settings" element={<Settings />} />
{/* 禁止:直接导入页面组件 */}
import Settings from './components/Settings';
<Route path="/settings" element={<Settings />} />

大型组件懒加载(推荐)

单个组件 > 100 KB 或依赖大型第三方库时使用懒加载。

已懒加载的组件

组件大小依赖
MermaidDiagram451 KBmermaid
HtmlPreviewPanel198 KBiframe sandbox
CodeEditor34 KBCodeMirror
Shell / StandaloneShell9 KBxterm

判断标准

条件是否懒加载
组件 bundle > 100 KB
依赖大型第三方库
非首屏必需
使用频率低
小型常用组件 (< 10 KB)
首屏必需核心组件
频繁切换的 UI 组件

Context 优化

非全局必需的 Context 应移到页面级组件:

{/* 好:页面级 Context */}
export function Settings() {
return (
<SettingsProvider>
<SettingsContent />
</SettingsProvider>
);
}

{/* 不好:全局加载不必要的 Context */}
<GlobalContext>
<Routes />
</GlobalContext>
warning

移动 Context 前必须分析依赖关系,确保不破坏跨页面状态共享。


Vendor Chunks 配置

当前策略按更新频率和使用频率分组:

{/* vite.config.js — manualChunks */}
{
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-ui': ['@radix-ui/react-context-menu', '@radix-ui/react-dialog', ...],
'vendor-icons': ['lucide-react'],
'vendor-utils': ['clsx', 'tailwind-merge', 'class-variance-authority', 'zustand'],
'vendor-markdown': ['react-markdown', 'remark-gfm', 'rehype-highlight'],
'vendor-codemirror': ['@codemirror/state', '@codemirror/view', ...],
'vendor-xterm': ['@xterm/xterm', '@xterm/addon-fit', '@xterm/addon-web-links'],
}

分组原则

类型更新频率缓存优先级示例
核心框架最高React, React DOM
大型第三方库CodeMirror, xterm
UI 组件库Radix UI, lucide
工具库clsx, zustand

新增依赖检查

添加新的第三方依赖时:

{/* 1. 检查大小 */}
npm info <package> dist.unpackedSize

{/* 2. 如果 > 100 KB,添加到 vendor chunks */}
{/* 3. 如果更新频率高,独立 vendor chunk */}
{/* 4. 确认支持 tree shaking */}
{/* 5. 运行 npm run analyze:build 检查影响 */}

Tree Shaking

正确的导入方式

{/* 好:具名导入(支持 tree shaking) */}
import { Button, Input, Select } from '@/components/ui';
import { Home, Settings, User } from 'lucide-react';

{/* 好:Radix UI 命名空间导入(官方推荐) */}
import * as Dialog from '@radix-ui/react-dialog';

{/* 不好:导入整个库 */}
import * as UI from '@/components/ui';
import * as Icons from 'lucide-react';

检查未使用导入

npm run typecheck # TypeScript 检测未使用导入
npm run lint:eslint # ESLint 警告未使用变量

Vite 生产构建配置

{/* vite.config.js — 关键配置 */}
build: {
minify: 'terser', // terser 比 esbuild 压缩率更高
sourcemap: false, // 生产环境关闭 sourcemap
chunkSizeWarningLimit: 500, // chunk 大小警告阈值 (KB)
terserOptions: {
compress: {
drop_console: true, // 移除 console.log(保留 warn/error)
drop_debugger: true, // 移除 debugger
},
},
}

常见问题与解决方案

主 bundle 过大 (> 500 KB)

排查步骤

  1. 运行 npm run build:renderer,打开 dist/stats.html
  2. 查看主 bundle 组成,找出最大模块
  3. 检查是否有未懒加载的大型组件
  4. 检查是否有不必要的全局导入

解决方案

  • 将大型组件改为懒加载
  • 移除未使用的导入
  • 将页面组件改为路由级懒加载

Vendor chunks 过大 (> 500 KB)

解决方案

  • 将大型 vendor 拆分成更小的 chunks
  • 按更新频率重新分组
  • 检查是否有重复打包的依赖

总 bundle 过大 (> 5 MB)

解决方案

  • 按需加载图表库(Mermaid、Cytoscape 等)
  • 移除不常用的功能或依赖
  • 考虑使用更小的替代库

代码分割失败

排查步骤

  1. 检查 lazy/ 目录中相应 split 文件的懒加载配置(page-level 在 lazy/pages.tsx、workspace bodies 在 lazy/workspaces.tsx、inline chat 组件在 lazy/inline.tsx
  2. 检查 App.tsx 中是否使用了懒加载组件
  3. 检查 Vite 配置中的 manualChunks

提交前检查清单

新增页面时

  • 页面组件已添加到 lazy/pages.tsx(或对应的 split 文件)
  • 使用 withSuspense 包裹并提供 fallback
  • App.tsx 中使用懒加载版本
  • 运行 npm run build:renderer 验证独立 chunk

新增大型组件时

  • 组件大小 > 100 KB → 使用懒加载
  • 依赖大型第三方库 → 使用懒加载
  • 提供合适的 loading 状态

新增第三方依赖时

  • 检查依赖大小
  • 依赖 > 100 KB → 添加到 vendor chunks
  • 更新频率高 → 独立 vendor chunk
  • 支持 tree shaking → 使用具名导入
  • 运行 npm run analyze:build 检查影响

持续优化建议

每次发版前

npm run verify:build
npm run build:renderer | grep -i "warning"
{/* 如果主 bundle 增加 > 50 KB,需要排查原因 */}

每月

npm run build:renderer
{/* 打开 dist/stats.html 检查可优化模块 */}
npm outdated
npm update

每季度

  • 审查所有 vendor chunks 配置
  • 评估新的优化策略
  • 评估是否需要移除不常用功能
  • 更新本规范文档

CI/CD 集成

建议在 CI 中添加 bundle 大小检查:

- name: Build and verify
run: |
npm run build:renderer
npm run verify:build || echo "Warning: Bundle size check failed"

性能指标基准

指标基准目标监控方式
主 bundle759 KB< 500 KBverify:build
首屏加载~2s< 2sLighthouse
总 bundle6.24 MB< 3 MBanalyze:build
Chunk 数量76-analyze:build

参考资源