前端性能优化方向完整清单(SSE 渲染合并与虚拟滚动)

AI 爆款文章创作器前端最值得优化的六个方向,按收益排序是:SSE 流式渲染合并、长列表虚拟滚动、Markdown 解析缓存、路由与组件代码分割、请求防抖节流、静态资源与图片懒加载。这个项目里流式渲染和 Markdown 解析属于”运行时性能”,直接影响打字机效果的流畅度;代码分割和资源优化属于”加载性能”,决定首屏白屏时长。下面每个方向都给出可运行的做法和项目里实际量到的改进。

一、性能问题先量化再动手

优化前先用两个工具定位瓶颈:rollup-plugin-visualizer 看打包体积构成,Chrome DevTools 的 Performance 面板记录流式生成时的长任务。项目早期发现首屏 bundle 超过 2MB,主因是 highlight.js 全量引入和 markdown-it 插件打包进主 chunk;运行时则确认 markdown-it 每次渲染全量解析是主线程长任务的源头。没有这些数据,优化方案会落到猜测上。

二、六个改进方向

2.1 SSE 流式渲染合并

方向 改动前 改动后
流式写入 每 chunk 触发渲染,FPS 掉到 20 以下 rAF 合并,稳定 60 FPS
Markdown 渲染 全文每次重渲染,3000 字耗时 80ms 段落级缓存,增量解析 <10ms
长列表 万级文章列表 DOM 上万节点,滚动卡顿 虚拟列表,DOM 稳定在 30 个左右

流式输出阶段,每收到一个 SSE chunk 就更新响应式状态,会触发整页依赖重渲染。前端在 store 里维护累积 buffer,用 requestAnimationFrame 批量提交,一帧内到达的所有 chunk 只触发一次视图更新。代码写法在状态管理方案里已给出,核心是”高频增量 → 低频批量”。

2.2 长列表虚拟滚动

文章管理页的历史记录是典型的万级长列表,一次性渲染所有 DOM 节点会让滚动掉帧。方案是只渲染可视区加缓冲区的条目。项目用 vue-virtual-scroller 的 RecycleScroller 处理不定高条目(文章卡片高度随标题行数变化),配置 min-item-size 做初始布局估算。实测从原生渲染 10000 条改为虚拟列表后,首屏渲染从秒级降到百毫秒级,滚动 FPS 从 18 左右回到 60。

<RecycleScroller
  class="article-list"
  :items="articles"
  :item-size="72"
  key-field="id"
>
  <template #default="{ item }">
    <ArticleCard :article="item" />
  </template>
</RecycleScroller>

配合虚拟列表的两个细节:v-for 的 key 用业务 id 而非索引,避免列表插入删除时整列重建;列表项里不做深层 reactive 绑定,改为 shallowRef 持有数组、整体替换触发更新。

2.3 Markdown 解析缓存

AI 生成的 Markdown 每次全文重渲染是最大的运行时浪费。优化分成两级:同一篇已渲染段落缓存 HTML,只对新追加的尾部做增量解析;文章从历史记录重新打开时直接读缓存,不走解析。加上 md.utils.escapeHtml 兜底代码块,解析链路本身在 3000 字文章上稳定在 10ms 内。

2.4 路由与组件代码分割

首屏只该加载登录页和骨架,创作工作台、编辑器、历史列表都按路由懒加载:

  1. 路由组件全部改 () => import('@/views/xxx.vue'),Vite 自动分包;
  2. 主入口按需引入 highlight.js 的 core 版 + 手动 registerLanguage 注册常用语言,语言包从数百 KB 降到几十 KB;
  3. 引入 rollup-plugin-visualizer 校验每个 chunk 构成,超过 200KB 的 chunk 拆分或按需加载。
// router 懒加载
const routes = [
  {
    path: '/generation',
    component: () => import('@/views/generation/index.vue'),
  },
  {
    path: '/history',
    component: () => import('@/views/history/index.vue'),
  },
]

改完首屏 bundle 从 2MB 降到约 300KB(gzip),登录页白屏时间明显缩短。生成工作台这类高频页可以加预加载——用户 hover 菜单项时触发 router.resolve(path).matched[0].components.default(),提前拉取 chunk。

2.5 请求防抖与节流

创作器有三个高频操作容易打爆接口:标题联想搜索、阶段切换时的自动保存、列表滚动触底的翻页加载。前两个用防抖(300ms 内只保留最近一次输入触发请求),翻页用节流或 IntersectionObserver 触底加载。防抖还能顺带减少服务端压力,一个搜索框在输入 20 个字符时只发 1 次请求而不是 20 次。

import { useDebounceFn } from '@vueuse/core'

const search = useDebounceFn(async (kw) => {
  const res = await fetch(`/api/titles?kw=${encodeURIComponent(kw)}`)
  suggestions.value = await res.json()
}, 300)

2.6 图片与静态资源优化

文章封面和模板缩略图是资源体积大头。三个动作按收益排序:WebP 替换(Vite 构建时用 vite-plugin-imagemin 转格式,体积平均降 60%);loading="lazy" + decoding="async" 让可视区外图片不阻塞;静态资源部署到 CDN 并配置 Cache-Control,历史封面第二次打开直接走强缓存。

三、优化后的验证

六个方向全部落地后,用 Lighthouse 跑一次前后对比:首屏 LCP 从 3.8s 降到 1.6s,Performance 面板里流式生成期间不再出现超过 50ms 的长任务,虚拟列表滚动稳定 60 FPS。验证要固定浏览器环境和网络条件再对比,否则数据没有说服力。

3.1 优先级排布

六个方向不该平均用力,项目里的实际排布分三批:第一批是流式渲染合并和 Markdown 解析缓存,解决打字机卡顿这类”用户立刻感知”的问题,改动量小收益最大;第二批是代码分割和虚拟列表,解决首屏时长和长列表滚动,属于加载与交互体验的底子;第三批是防抖节流和图片优化,属于锦上添花,可以在前两批稳定后逐步补齐。反过来如果先做图片优化,流式生成照样卡,用户体感不会有变化。

3.2 不建议做的优化

有两个方向在创作器里被验证为负收益:一是给所有组件套 v-memo,列表项本来就低频变更,缓存命中率低反而增加 diff 判断成本;二是过度拆分 chunk,把每个工具函数单独打包,HTTP 请求数暴增,弱网下加载更慢。优化的前提是量化证据,没有长任务和体积数据支撑的方案先不落地。

常见问题(FAQ)

Q1:性能优化先做哪一项?

先用 visualizer 和 Performance 量化,流式渲染合并和代码分割收益最大、成本最低。

Q2:虚拟列表和分页怎么选?

万级以上且滚动浏览用虚拟列表,千级以下直接分页,避免引入库的维护成本。

Q3:Markdown 渲染卡顿怎么定位?

Performance 面板录制生成过程,长任务若在 markdown 解析函数里,就上段落级缓存。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
小码农的头像小码农认证作者

相关推荐

返回顶部