用 Vue 3 Composition API 重构个人博客前端 | 拾音
为什么要重构
旧版博客基于 Vue 2 与 Webpack 构建,页面逻辑散落在 data、methods、watch 三个选项里。随着文章详情页增加目录、阅读进度、评论三个交互,单个组件膨胀到 600 多行,任何一处修改都要在选项之间来回跳转。
Vue 3 的 Composition API 允许把"按选项组织"改为"按关注点组织",这正是重构的切入点。
组合式函数的拆分方式
我将页面能力拆成三个可复用的组合式函数:
useArticle(slug):负责请求文章、处理加载与错误状态;
useToc(html):从渲染后的 HTML 中提取标题,生成目录并监听滚动;
useReadingProgress():基于滚动位置计算阅读进度。
export function useArticle(slugRef) {
const article = ref(null);
const loading = ref(false);
const error = ref('');
watchEffect(async () => {
loading.value = true;
error.value = '';
try {
article.value = await api.getArticle(slugRef.value);
} catch (e) {
error.value = e.message;
} finally {
loading.value = false;
}
});
return { article, loading, error };
}
组合式函数返回的是响应式引用,调用方可以自由组合,而不必关心内部的请求时序。
状态收敛与请求缓存
重构前,首页、标签页、归档页各自维护一份文章列表,切换路由就会重复请求。重构后引入 Pinia 保存站点级数据(站点配置、标签、文章索引),并用 Map 做按 slug 的请求缓存,页面切换的重复请求下降了约 60%。
经验:不要把所有状态都塞进全局 store。只有跨页面共享、生命周期长于组件的状态才值得提升。
构建体积优化
Vite 的产物分析显示,首屏最大的依赖是 Markdown 渲染器与代码高亮。我做了两件事:
- 把 Markdown 渲染留在服务端,前端只接收 HTML;
- 代码高亮按需动态导入,仅在文章页加载。
最终首屏 JS 从 312 KB 降到 146 KB(gzip 后 48 KB),Lighthouse 性能分从 78 提升到 94。
小结
重构不是把 setup() 套在旧代码外面,而是重新划分关注点。组合式函数让文章请求、目录、进度条这些能力可以独立测试和复用,也为后续接入媒体播放页留出了扩展空间。