vue 3 模板中 {{ count }} 自动解包为 {{ count.value }},由编译器静态处理,无运行时开销;该便利仅限模板内,script 中仍需显式使用 .value。

因为 Vue 3 的模板编译器在编译阶段就做了自动解包处理——它识别出变量是 ref 类型,会悄悄把 {{ count }} 编译成等价于 {{ count.value }} 的逻辑,整个过程不经过运行时,也没有额外性能开销。
编译器提前介入,不是运行时魔法
Vue 模板不是直接执行 JavaScript,而是先被编译成 render 函数。比如你写:
{{ msg }}
实际会被编译为类似这样的代码片段:
_toDisplayString($setup.msg)
注意:这里传入的是 $setup.msg,不是 $setup.msg.value。但 _toDisplayString 内部会自动读取 ref 的 .value。这个行为由编译器固化在生成的 render 函数里,属于静态转换,和 Proxy 或 getter 拦截无关。
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
仅限模板上下文,JS 逻辑中不适用
这种便利只存在于 <template></template> 标签内。一旦进入 script、watch、computed、onMounted 等 JS 执行环境,就没有编译器帮你补 .value 了:
-
console.log(count)打印的是整个 ref 对象({ value: 0 }) -
count++会报错,必须写count.value++ -
watch(count, () => {})虽然能监听,但那是 Vue 内部对 ref 的特殊支持,不是语法糖
事件处理器里也能直接赋值,原理相同
你在模板中写 @click="msg = 'new'",看着像直接赋值,其实编译后是:
$setup.msg = 'new'
而 Vue 的响应式系统已对 ref 的 .value 做了 setter 代理,所以这行代码等价于 $setup.msg.value = 'new'。这也是为什么不能在模板里写 {{ msg.value }}——它虽不报错,但绕过了 Vue 的解包机制,属于冗余且不推荐的写法。
对比 reactive,更能看清设计意图
reactive 返回的是 Proxy 对象,属性天然可读可写,不需要 .value;而 ref 是为兼容基本类型(number/string/boolean)设计的封装容器。加 .value 是明确告诉开发者:“你操作的不是一个原始值,而是一个响应式引用”。模板里省略它,是为了写起来更接近直觉;script 里保留它,是为了避免误操作(比如把整个 ref 对象当普通对象解构或传递)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










