大型列表场景应优先使用 shallowref 替代 ref,仅追踪 .value 赋值变化,减少60%–70%初始化开销;整批替换数据时适用,局部更新需配合 triggerref;避免循环中频繁访问 .value,改用 computed 缓存;静态字段用 markraw 隔离响应式;单条记录可变字段宜用 toref 而非全量 torefs。

在大型列表场景中,ref 的默认行为会带来明显内存和性能负担——它会对整个对象做深度响应式代理,而列表渲染往往只需要顶层引用变化感知。关键不是少用 ref,而是选对 ref。
优先用 shallowRef 替代 ref 处理列表数据
shallowRef 只追踪 .value 本身的赋值变化,不递归代理内部属性。对于万级表格、动态加载的滚动列表,能直接减少 60%–70% 的初始化开销和内存占用。
- 适用场景:整批替换数据(如分页刷新、搜索重载),且你不需要监听某一行内部字段的变更
- 写法示例:
const listData = shallowRef([]),后续通过listData.value = newData触发更新 - 注意:如果需要局部更新某一项(比如勾选状态),得配合
triggerRef(listData)手动通知视图刷新
避免在循环中频繁访问 .value
每次读取 item.value.xxx 都会触发依赖收集;列表越长,收集成本越高。模板中看似简洁的 {{ item.name }},背后可能是上千次响应式访问。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 优化思路:把计算逻辑提前收口,用
computed缓存结果,而不是在模板里反复读 .value - 反例:
function getDisplayName() { return user.value?.profile?.name || '-' }—— 每次调用都访问 .value - 正例:
const displayName = computed(() => user.value?.profile?.name || '-')—— 仅在依赖变化时重新计算
静态/只读字段用 markRaw 隔离响应式系统
列表中常包含不会变动的元信息(如图标配置、枚举映射、API 返回的 schema 描述),这些对象一旦被 ref 包裹,就会被 Vue 递归代理,纯属浪费。
- 写法:
const staticConfig = markRaw({ iconMap, statusText }),再传给shallowRef或直接作为普通属性使用 - 效果:跳过响应式转换,节省内存,也避免因误改导致意外更新
- 典型位置:表格列定义(columns)、筛选条件配置、国际化文案对象
解构时用 toRefs 保响应性,但别滥用
从 reactive 对象解构会丢失响应性,但直接用 ref + toRefs 在大型列表里也可能增加冗余包装。
- 推荐做法:对列表整体用
shallowRef,对单条记录的可变字段(如 selected、expanded)单独用ref或toRef - 例如:
const row = reactive({ id: 1 }); const isSelected = toRef(row, 'selected'),比每个字段都 ref 更轻量 - 避免:
const { id, name, selected } = toRefs(row)—— 创建多个 ref 实例,无必要开销
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









