shallowRef 是 Vue 3.4+ 提供的轻量级响应式 API,仅使 .value 的赋值和读取响应,不代理嵌套属性,适用于大型不可变数据、DOM 元素、第三方实例等场景。

shallowRef 是 Vue 3.4+ 引入的轻量级响应式 API,专为“值本身需要响应式,但内部属性不需要深层追踪”的场景设计。它不代理对象的嵌套属性,只让 .value 的赋值和读取具备响应性,适合处理大型不可变数据、第三方库实例、DOM 元素或结构扁平的简单对象。
什么时候该用 shallowRef 而不是 ref?
当你有以下情况时,shallowRef 更合适:
- 数据是只读的(如从 API 获取的静态配置对象),你只关心整个对象是否被替换,不监听其字段变化
- 对象层级深、体积大(如含数百个键的 JSON 数据),用 ref 会触发大量不必要的 proxy 初始化和依赖收集,拖慢性能
- 持有非 reactive 兼容对象(如 Map、Set、CanvasRenderingContext2D、原生 DOM 元素),ref 会尝试深度转换并报错或失效,shallowRef 则安全包裹
- 配合
toRaw或markRaw使用,明确告诉 Vue “别碰这个值的内部”
基本用法与行为对比
对比 ref({ a: 1, b: { c: 2 } }) 和 shallowRef({ a: 1, b: { c: 2 } }):
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
- 两者都可在模板中通过
{{ state.value.a }}访问,且修改state.value = 新对象都能触发更新 - 但修改
state.value.b.c = 3:ref 版本会响应(因为深层劫持),shallowRef 版本不会触发视图更新 - 访问
state.value.b返回的是原始对象,未被 reactive 包装;若需对b单独响应,应单独用ref或shallowRef管理
典型实用场景示例
比如在组件中缓存一个渲染用的 Canvas 上下文:
立即学习“前端免费学习笔记(深入)”;
import { shallowRef, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const canvasCtx = shallowRef(null)
onMounted(() => {
const canvas = document.getElementById('my-canvas')
canvasCtx.value = canvas.getContext('2d') // ✅ 安全赋值,无 proxy 开销
})
onUnmounted(() => {
canvasCtx.value = null
})
// 模板中可直接用 canvasCtx.value.fillRect(...),无需担心响应式干扰
return { canvasCtx }
}
}
又如管理一个仅需整体替换的配置项:
const config = shallowRef({ theme: 'light', lang: 'zh' })
// 后续只需 config.value = newConfig,无需担心 theme/lang 字段被意外修改触发更新
注意事项与搭配建议
- 不要在
shallowRef的.value上直接调用reactive或ref—— 它已是最外层响应式容器,嵌套使用反而增加冗余 - 若需部分深层响应,推荐拆分:例如
{ meta: shallowRef(...), data: ref(...) } - 与
computed配合时注意:计算属性依赖shallowRef.value时,仅在.value被重新赋值时才重新求值,内部属性变更无效 - 调试时可通过
isShallow工具函数判断是否为 shallow 类型

















