当前位置: 首页 > news >正文

Vue 性能优化策略

文章目录

  • 前言
  • 一、优化原则
    • 1.1 先测量,再优化
    • 1.2 优化层次
  • 二、渲染层优化
    • 2.1 合理使用 key
    • 2.2 v-once:静态内容只渲染一次
    • 2.3 v-memo:条件缓存子树(Vue 3.2+)
    • 2.4 v-memo vs computed
    • 2.5 v-if vs v-show
    • 2.6 减少组件嵌套与无效 render
  • 三、响应式层优化
    • 3.1 shallowRef / shallowReactive
    • 3.2 markRaw:标记永不代理
    • 3.3 减少 reactive 范围
    • 3.4 computed 缓存 vs watch
  • 四、代码与加载优化
    • 4.1 路由懒加载
    • 4.2 组件异步加载
    • 4.3 Tree-shaking
    • 4.4 KeepAlive 缓存页面
  • 五、大列表:虚拟滚动
    • 5.1 问题
    • 5.2 方案
    • 5.3 配合优化
  • 六、其他实用手段
    • 6.1 防抖 / 节流
    • 6.2 Web Worker
    • 6.3 图片与资源
    • 6.4 编译层(框架内置)
  • 七、面试聚焦
    • 7.1 优化应基于实际测量
    • 7.2 shallowRef 的 .value 变化会触发更新吗?
    • 7.3 v-memo 和 computed 的区别?
    • 7.4 v-once 和静态提升的区别?
  • 八、易混淆点
  • 九、思考与练习
  • 总结

前言

Vue 3 本身已有编译优化和 Proxy 响应式,但业务场景仍可能出现大列表、频繁更新、首屏过慢等问题。本篇从渲染、数据、代码、测量四个层面梳理 Vue 性能优化策略。本篇会讲清楚:

  • shallowRef / shallowReactive / markRaw
  • v-once / v-memo 的使用与区别
  • 大列表虚拟滚动与懒加载
  • 优化应基于实际测量

一、优化原则

1.1 先测量,再优化

// 不要凭感觉优化// 1. Chrome Performance 面板 → 录制交互,看 Long Task、Layout、Paint// 2. Lighthouse → FCP、LCP、TBT// 3. Vue DevTools → 组件 render 次数、耗时
原则说明
有数据再动手定位瓶颈是 render、网络还是计算
避免过早优化简单页面不必堆 v-memo、shallowRef
优先低成本懒加载、合理 key 往往比改架构见效快
关注用户感知LCP、INP 比纯 JS 执行时间更重要

1.2 优化层次

编译层(Vue 3 内置)→ 静态提升、PatchFlag、Block Tree 渲染层 → key、v-once、v-memo、虚拟滚动 响应式层 → shallowRef、markRaw、减少 reactive 范围 代码层 → 路由/组件懒加载、Tree-shaking

二、渲染层优化

2.1 合理使用 key

列表用稳定唯一 id作 key,减少错误复用和不必要 DOM 操作(详见《Key 的作用与原理》)。

2.2 v-once:静态内容只渲染一次

<header v-once> <h1>{{ title }}</h1> <nav>固定导航</nav> </header>
  • 首次渲染后跳过该子树后续所有更新(含 props 变化)
  • 适合页脚、版权、固定说明等几乎不变的内容
  • 与编译期静态提升不同:v-once 是运行时指令,开发者手动标记

2.3 v-memo:条件缓存子树(Vue 3.2+)

<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]"> <ExpensiveItem :item="item" /> </div>
  • 依赖数组不变→ 跳过该子树更新(含子组件)
  • 依赖变化→ 正常更新
  • 适合大列表中昂贵子组件,且只有部分字段影响展示
<!-- 选中态变化才重渲染该项 --> <div v-memo="[item.id, item.selected]"> <ItemCard :item="item" /> </div>

2.4 v-memo vs computed

对比项v-memocomputed
作用位置模板子树script 逻辑
跳过整段模板 + 子组件 render重复计算
依赖显式数组[a, b]自动收集

v-memo 管「这块模板要不要重绘」;computed 管「这个值要不要重算」。

2.5 v-if vs v-show

场景推荐
频繁切换显示v-show(只改 display)
很少出现v-if(不渲染 DOM)

2.6 减少组件嵌套与无效 render

  • 不要把大对象整包 reactive 后传给大量子组件
  • v-memo、拆分组件、Pinia 按需订阅减少无关更新

三、响应式层优化

3.1 shallowRef / shallowReactive

import{shallowRef,shallowReactive,triggerRef}from'vue'// 只代理 .value 引用变化,不深度代理内部constbigData=shallowRef({list:[...10000items]})// ✅ 替换整个对象 → 触发更新bigData.value={list:newList}// ❌ 改深层属性 → 不触发更新bigData.value.list.push(item)// 手动触发bigData.value.list.push(item)triggerRef(bigData)
API深度代理触发更新
ref是(对象)深层变化也触发
shallowRef.value替换触发
reactive深层变化触发
shallowReactive仅第一层仅第一层属性触发

场景:第三方库实例、大型不可变数据、实时 tick 数据批量替换。

3.2 markRaw:标记永不代理

import{markRaw,reactive}from'vue'constchart=markRaw(echarts.init(dom))conststate=reactive({chart,// 不会被 proxy,避免无效依赖option:{}})

适合 ECharts、地图 SDK 等不需要响应式的大对象。

3.3 减少 reactive 范围

// ❌ 整个表单大对象都 reactiveconstform=reactive({/* 50 个字段 */})// ✅ 只有会驱动视图的字段 reactiveconstvisibleFields=reactive({name:'',status:''})conststaticConfig={/* 只读配置,普通对象 */}

3.4 computed 缓存 vs watch

// ✅ 派生数据用 computed,有缓存constfiltered=computed(()=>list.value.filter(i=>i.active))// ✅ watch 默认懒执行,避免 immediate 滥用watch(source,handler)// 默认 flush: 'pre',按需触发

四、代码与加载优化

4.1 路由懒加载

{path:'/report',component:()=>import('@/views/Report.vue')}

按路由拆 chunk,减小首屏 JS(详见《路由懒加载》)。

4.2 组件异步加载

constHeavyChart=defineAsyncComponent(()=>import('./HeavyChart.vue'))

弹窗、Tab 内重型组件按需加载(详见《动态组件与异步组件》)。

4.3 Tree-shaking

  • 按需导入:import { ref } from 'vue',避免全量
  • 工具库:import debounce from 'lodash-es/debounce'
  • 构建工具生产模式自动 tree-shake

4.4 KeepAlive 缓存页面

Tab、列表→详情→返回场景缓存实例,避免重复创建(详见《KeepAlive 缓存组件》)。注意max控制内存。


五、大列表:虚拟滚动

5.1 问题

渲染 1 万条<li>→ 1 万个 DOM 节点 → 卡顿、内存高。

5.2 方案

只渲染可视区域+ 少量缓冲区的项:

<script setup> import { RecycleScroller } from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' </script> <template> <RecycleScroller :items="list" :item-size="50" key-field="id" v-slot="{ item }" > <div class="row">{{ item.name }}</div> </RecycleScroller> </template>

常用库:vue-virtual-scroller@tanstack/vue-virtual

5.3 配合优化

  • 列表项用v-memo="[item.id, item.xxx]"
  • 项内避免深层 reactive
  • 稳定key用业务 id

六、其他实用手段

6.1 防抖 / 节流

搜索输入、scroll、resize 监听使用 debounce/throttle,减少 render 和请求次数。

6.2 Web Worker

大 JSON 解析、复杂计算放 Worker,避免阻塞主线程 UI。

6.3 图片与资源

  • 懒加载:loading="lazy"、v-lazy 指令
  • 合适格式:WebP、压缩、CDN
  • 路由 prefetch 空闲预加载(详见《路由懒加载》)

6.4 编译层(框架内置)

Vue 3 模板自动静态提升、PatchFlag、Block Tree,无需手写(详见《编译优化》)。手写 render 函数享受不到,优先写模板。


七、面试聚焦

7.1 优化应基于实际测量

Performance、Lighthouse、Vue DevTools 定位瓶颈,避免盲目 v-memo。

7.2 shallowRef 的 .value 变化会触发更新吗?

shallowRef.value = newObj触发更新;改value内部深层属性不会,需triggerRef或整体替换。

7.3 v-memo 和 computed 的区别?

v-memo 跳过模板子树 render;computed 缓存计算结果。一个管视图,一个管逻辑。

7.4 v-once 和静态提升的区别?

静态提升是编译期自动识别静态节点;v-once 是运行时指令,手动标记且跳过后续所有更新。


八、易混淆点

  1. shallowRef 不是不更新:换.value会更新,只是不深追踪内部。
  2. v-once 后 props 变也不更新:确认内容真正静态再用。
  3. v-memo 依赖要写全:漏写依赖会导致该更新不更新。
  4. 虚拟滚动不减少数据量:只减少 DOM 数量,接口仍可能要分页。
  5. KeepAlive 占内存:需maxinclude控制。

九、思考与练习

1.大列表卡顿如何优化?

解析:虚拟滚动减少 DOM;稳定 key;v-memo 昂贵子项;分页或懒加载数据。

2.shallowRef 适用场景?

解析:大型对象整体替换、第三方实例、频繁替换 .value 但不关心深层变化的场景。

3.v-memo 依赖数组如何写?

解析:列出影响该子树展示的所有响应式值,如[item.id, item.status]

4.首屏 JS 过大怎么办?

解析:路由懒加载、异步组件、Tree-shaking、分析 bundle(rollup-plugin-visualizer)。

5.为什么 say「先测量再优化」?

解析:避免无效优化增加复杂度;不同瓶颈方案不同(网络 vs render vs 计算)。


总结

  • 原则:先测量,再针对性优化,避免过早优化
  • 渲染:key、v-once、v-memo、v-show/v-if、虚拟滚动
  • 响应式:shallowRef、markRaw、缩小 reactive 范围、computed 缓存
  • 代码:路由/组件懒加载、Tree-shaking、KeepAlive
  • 本质:减少 DOM 数量、减少无效 render、减小首屏体积
http://www.cnnetsun.cn/news/3053471.html

相关文章:

  • 解决 Python 依赖冲突,ROCm 环境下安装深度学习库的技巧
  • 依赖引入与适用场景
  • python爬虫实战项目|第97篇:爬虫系统测试与持续集成
  • 企业网络安全立体防线:DDoS、CC、XSS与ARP攻击防御实战
  • RLHF的原罪:当AI对齐撞上Arrow不可能定理
  • Spring Boot接口防探测实战:从信息泄露到多层安全加固
  • FactoryBluePrints:3000+戴森球计划工厂布局的终极解决方案
  • 计算机网络体系结构-网络原理初识
  • 告别手速焦虑:大麦抢票自动化终极解决方案
  • 打破游戏控制器兼容性壁垒:GlosSI系统级Steam Input解决方案
  • 第87题 氮化镓(GaN)自支撑衬底氢化物气相外延(HVPE)裂纹与翘曲控制技术
  • 3个实用步骤让Win11Debloat彻底优化你的Windows系统性能
  • Web安全测试:动态URL参数收集与智能漏洞探测实战
  • TUSB4020B评估模块拆解:从电源设计到信号完整性,打造稳定USB集线器
  • OpenCore Legacy Patcher完整指南:让旧Mac免费升级最新macOS的终极方案
  • 竞赛云端网络 华为DevStation昇腾WebIDE能否安装腾讯CodeBuddy claude
  • 春考:把握升学新通道,走出更适合自己的成长路径
  • 一文读懂AI落地的三驾马车:大语言模型、RAG、多模态AI
  • BiliTools跨平台哔哩哔哩工具箱:高效下载与管理B站资源的终极指南
  • 德州仪器Value Soundbar参考设计:8周量产的高集成音频方案解析
  • 山东春考网课:让升学备考更高效、更有方向
  • 论文党速看!2026亲测靠谱的AI论文写作工具|安心版
  • 海外华商选型指南:越南批零跨境生意,如何选择适配的进销存收银系统
  • Nmap与Kali Linux实战指南:从网络扫描到自动化渗透测试
  • Hudi Metadata Table 与 Hive Sync (HMS)怎么选?
  • 牛津大学让AI学会“物理直觉“:无需看视频就能预测物体运动
  • TLV320AIC27音频编解码器评估:硬件配置、工作模式与DSP集成实战
  • 实操-大白菜的五个实操
  • Whois域名查询API集成指南:从零搭建域名信息查询工具
  • Win11Debloat:3分钟完成Windows系统优化,彻底清理臃肿应用