Vue 项目常见性能优化手段有哪些?
简化版
Vue 性能优化包括合理拆分组件、避免不必要响应式、使用 computed 缓存、v-if/v-show 合理选择、列表 key 稳定、虚拟列表、异步组件、路由懒加载、keep-alive 缓存、减少深度 watch 和大对象响应式开销。
详细版
常见手段:
- 路由懒加载:
const User = () => import('./User.vue');
- 大列表使用虚拟滚动。
- 频繁切换组件用
keep-alive。 - 派生数据用 computed,不在模板里写重计算函数。
- 大型不可变数据用
shallowRef、markRaw减少深层代理。 - 避免
deep: true监听大对象。 - v-for 使用稳定 key。
面试回答最好按“渲染减少、包体减少、响应式成本减少、缓存复用”几个方向组织。
完整版教学
一、先定位瓶颈再优化
性能优化不能只背清单。要先判断问题是首屏慢、交互卡、大列表卡、包体大,还是接口慢。不同瓶颈对应不同方案。
Chrome Performance、Vue Devtools、打包分析工具都能帮助定位。
二、减少不必要渲染
组件拆分要合理,状态尽量放在真正需要它的地方。父组件状态一变,子树可能重新渲染。稳定 props、合理 computed、避免模板中调用重函数,都能减少更新成本。
v-memo、v-once 在特定场景也能减少重复渲染,但不要滥用。
三、大列表要虚拟滚动
如果一次渲染几千条 DOM,任何框架都会吃力。虚拟列表只渲染可视区域附近的项目,大幅减少 DOM 数量。
这是列表性能优化最有效的方向之一。
四、降低响应式成本
Vue3 会递归代理对象。对于大型静态配置、第三方实例、图表对象,不一定需要深层响应式。
可以用:
const data = shallowRef(bigData);
const chart = markRaw(instance);
这样避免无意义的深层代理和追踪。
五、面试追问与工程落地
Vue 性能题常追问“如何证明优化有效”。不要只说用了懒加载、keep-alive,而要能说用打包分析看 chunk 体积,用 Performance 看长任务,用 Vue Devtools 看组件更新时间,用接口瀑布图看首屏瓶颈。性能优化需要数据闭环。
还会问 keep-alive 的副作用。缓存组件能减少重复创建,但会占用内存,组件不会真正 unmounted,而是进入 activated/deactivated 生命周期。里面的定时器、订阅、数据刷新策略要重新设计,否则可能出现后台还在跑任务的问题。
工程里首屏优化和交互优化要分开。首屏关注包体拆分、SSR/SSG、资源压缩、骨架屏、接口并发;交互关注组件更新范围、大列表虚拟化、事件节流、响应式数据规模。不同问题不能用同一个答案糊过去。
六、建立指标、归因、改动、复测闭环
性能结论必须对应可测指标。加载性能关注 LCP、INP、资源体积和请求瀑布,更新性能关注长任务、组件更新时间、DOM 数量和每次交互触发的组件范围;先用生产构建复现,再用 Chrome Performance、Vue Devtools 和 bundle analyzer 找主要贡献者。
基线测量 → 找最大瓶颈 → 一次改一个变量 → 同环境复测
↑ │
└──────────── 无收益则回退 ────────────┘
假设列表有 10000 项,每项生成 5 个 DOM 节点,一次全量渲染约 50000 个节点;屏幕实际只显示 30 项,虚拟列表加缓冲区渲染 50 项,只需约 250 个节点,节点数量约降到原来的 0.5%。这类数量级削减通常比在单个渲染函数里做微小优化更有效。
| 瓶颈 | 证据 | 对应策略 |
|---|---|---|
| 首屏 JS 大 | chunk 体积、下载和执行长 | 路由懒加载、依赖审计、SSR/SSG |
| 列表更新慢 | DOM 数量大、长任务明显 | 虚拟列表、稳定 key、减少组件层级 |
| 子项大面积更新 | Vue profiler 显示整表重渲染 | 保持 props 稳定、下沉 active 布尔值 |
| 深层数据读取重 | 大数组访问触发大量 Proxy trap | shallowRef + 不可变根替换 |
| 缓存页后台耗时 | deactivated 后计时器仍运行 | 激活/停用钩子管理副作用 |
Vue 官方指出深层响应式开销通常在一次渲染访问十万级属性时才值得专项处理。shallowRef 是用“嵌套数据按不可变值处理”换取较少追踪:直接 push 或改深层字段不会触发更新,必须替换根值;没有这个更新纪律,优化会变成功能 bug。
keep-alive 也不是免费加速。缓存 20 个重型页面可能保留 20 份组件状态、DOM 和资源;应使用 include/exclude、max 或业务淘汰策略,并在 deactivated 时暂停不需要的轮询和订阅。
性能优化的交付物不是“用了哪些 API”,而是基线、瓶颈证据、改动前后数据和副作用评估。
七、常见误区与追问
- 误区:组件拆得越细性能一定越好。 每个组件实例也有开销,过度抽象在大列表中可能制造成百上千个额外实例。
- 误区:keep-alive 能无成本缓存任意多页面。 缓存会占内存并改变生命周期,后台任务和失效策略都要管理。
- 误区:所有大型对象都应该 markRaw。 只有确定不需要响应式更新的对象才适合退出代理,否则视图不会随修改更新。
- 追问:怎样证明一次 Vue 优化有效? 在相同数据、设备和生产构建下对比指标,并确认没有把成本转移到内存或后续交互。
- 追问:为什么稳定 props 能减少子组件更新? 子组件只在接收的 prop 变化时需要更新,把 active 结果在父层算好可避免所有行都接收变化的 activeId。
- 追问:虚拟列表的代价是什么? 需要管理滚动窗口、项目高度、焦点与可访问性,动态高度列表实现更复杂。
- 追问:shallowRef 更新嵌套字段为什么没反应? 浅层 API 只追踪
.value根访问,必须用新根对象或数组替换才能触发。
八、加强记忆
Vue 性能优化按四类记:少渲染、少加载、少追踪、多复用。具体就是懒加载、虚拟列表、computed、稳定 key、keep-alive、shallowRef/markRaw,以及谨慎 deep watch。
任何优化都要留下同条件的前后指标,并同时检查交互延迟、内存和实现复杂度有没有恶化。