协同笔记系统端到端性能调优:SPA 路由 404、Lighthouse 95+、Tree-shaking 与 Bundle 分包实战
记录协同笔记项目的排查与优化:配置 SPA 路由重写解决刷新 404,按需注册 highlight.js 语言包并用 lodash-es 与 manualChunks 缩减首屏体积。
· 9 分钟
本页目录展开 / 收起
读完你能做什么
- 把“刷新 404”“首包过大”“输入或加载变慢”拆成不同故障域。
- 用路由回退、依赖分析和按需加载分别解决对应问题。
- 建立优化前基线,用构建产物与 Lighthouse 复测结果证明收益。
本文不是一组互不相关的技巧,而是一条诊断链:先确认请求是否到达正确页面,再确认浏览器下载了什么,最后确认主线程何时可交互。每一步只解决一个已测量的问题。
协同笔记项目刚上线时,最先暴露的往往是两个直观问题:一是在子路由一按刷新页面就直接 404;二是首屏静态资源包体积逼近 2MB,在弱网下白屏时间很长。这篇文章记录这两个问题的排查过程与瘦身改动。
痛点一:解决 SPA 路由刷新 404 难题
使用 vue-router 或 react-router 的 HTML5 createWebHistory() 模式时,URL 路径如 /notes/doc-123 并非静态服务器磁盘上的真实物理目录。
当用户首次访问首页正常,但在子路由手动刷新页面时,Vercel / Nginx 尝试寻找 out/notes/doc-123/index.html 失败,从而抛出 404。
解决方案:配置 SPA 重写规则
在项目根目录创建 vercel.json,配置路由通配重写,将所有路径回退至 index.html:
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}若使用 Nginx 部署,则在配置文件中添加:
location / {
try_files $uri $uri/ /index.html;
}痛点二:Bundle 瘦身与 Tree-Shaking 实操
通过 rollup-plugin-visualizer 生成的打包产物依赖图谱分析,我们发现了几个严重拖慢打包体积的“元凶”:
优化前产物分布:
┌───────────────────────────────────────────────────────────┐
│ vendor.js (1.85 MB) │
│ ├── highlight.js 全量 180+ 语言包 (占 820 KB) ──► 严重冗余! │
│ ├── lodash 全量 CommonJS 打包 (占 280 KB) ────► 无法摇树! │
│ └── tiptap 未使用的重型扩展 (占 450 KB) │
└───────────────────────────────────────────────────────────┘1. 彻底替换 lodash 为 lodash-es
传统 lodash 是 CommonJS 格式,打包工具(如 Rollup / Webpack)无法对其进行精确的静态符号分析,即便只写 import { debounce } from 'lodash',也会将数十个不相关的工具函数全部打入产物。
- 优化措施:迁移到原生 ESM 的
lodash-es:BASH pnpm remove lodash @types/lodash pnpm add lodash-es pnpm add -D @types/lodash-es - 效果:仅此一项,产物体积直接缩减 240 KB。
2. highlight.js 按需注册常用语言
Tiptap 的代码高亮扩展默认若引入了 lowlight/all,会将 180 多种几乎用不到的冷门语言定义全部打包。
- 优化措施:改用
common常见语言集或手动按需注册:
import { createLowlight } from 'lowlight';
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import python from 'highlight.js/lib/languages/python';
import json from 'highlight.js/lib/languages/json';
import css from 'highlight.js/lib/languages/css';
const lowlight = createLowlight();
// 仅注册前端和主流后端高频使用的 5~8 种核心语言
lowlight.register('javascript', javascript);
lowlight.register('typescript', typescript);
lowlight.register('python', python);
lowlight.register('json', json);
lowlight.register('css', css);
export { lowlight };- 效果:代码高亮相关模块体积从 820 KB 骤降至 65 KB。
3. Vite / Rollup 手动分包(Manual Chunks)
在 vite.config.ts 中配置精细化分包,将低频变动的大型基础库独立为长效缓存 Chunk:
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'tiptap-vendor': ['@tiptap/vue-3', '@tiptap/starter-kit', 'prosemirror-state', 'prosemirror-view'],
'yjs-collab': ['yjs', '@hocuspocus/provider', 'y-indexeddb'],
'ui-icons': ['lucide-vue-next'],
},
},
},
// 开启现代浏览器 esnext 构建与 terser 压缩
target: 'esnext',
minify: 'terser',
},
});痛点三:Lighthouse 体验综合优化
通过 Google Lighthouse 进行全维度巡检并逐项修复:
- Accessibility(无障碍):
- 在根
index.html的<html>标签上显式指定lang="zh-CN"; - 为编辑器工具栏图标按钮添加
aria-label="加粗"、aria-label="插入链接"。
- 在根
- SEO 与 Meta:
- 补充标准的 OpenGraph 和 Twitter Card 元标签;
- 自动生成
robots.txt与sitemap.xml。
- 最终成效:
- Vendor 首屏 JS 体积从 1.85MB 压减至 320KB(Gzip 后约 90KB);
- Lighthouse 性能得分从 58 分跃升至 96 分;
- 首屏 FCP 进入 0.8 秒 内。
官方资料
优化复盘与建议
- 静态部署一定要做兜底重写:SPA 采用 HTML5 History 路由时,服务端的
try_files或rewrites是必须配置的基础设施,否则任何带参数的深层链接分享给别人都是 404。 - 警惕巨石库的全量引用:富文本生态中像
highlight.js、lowlight或lucide这类工具,一旦没有配置按需打包,分分钟把 100 多种未用语言或几千个图标全部打入首包。每次引入第三方库后,在 Visualizer 里看一眼体积应该成为打包习惯。