前言
vue中的计算属性相关知识:
核心概念对比
特性 | 脚本环境(JS) | 模板环境(Template) |
---|---|---|
ref 访问 |
.value 必需 |
自动解包(无需 .value ) |
computed 访问 |
.value 必需 |
自动解包(无需 .value ) |
原始对象访问 | 直接访问 | 直接访问 |
二话不说先上代码,大家觉得会输出什么呢?
JS
<template>
<div>{{ firstName }}+{{lastName}}</div>
<div>{{ name }}</div>
<div>{{ name.value }}</div>
</template>
<script setup>
import { ref, computed } from "vue";
const firstName = ref("ac");
const lastName = ref("bc");
console.log(firstName);
const name = computed(() => {
return firstName.value + lastName.value;
});
console.log(name);
console.log(name.value);
</script>
<style>
</style>
结果如下所示,大家可能有点疑惑?接下来就看下面知识点:
js
const firstName = ref("ac");
// 创建响应式引用,value = "ac"
const lastName = ref("bc");
// 创建响应式引用,value = "bc"
控制台首次输出
javascript
console.log(firstName); // 输出 RefImpl 对象
console.log(name); // 输出 ComputedRefImpl 对象
console.log(name.value); // 输出计算值 "acbc"
模板渲染机制
模板编译结果对比
模板写法 | 实际渲染值 | 原理说明 |
---|---|---|
{{ firstName }} |
"ac" | 自动解包 ref,等价于 firstName.value |
{{ name }} |
"acbc" | 自动解包 computed,等价于 name.value |
{{ name.value }} |
空/undefined | 错误用法,因为 name 解包后得到字符串,字符串没有 .value 属性 |
为什么表现不同?
-
Ref 对象结构:
typescriptinterface Ref<T> { value: T __v_isRef: true // ...其他内部属性 }
-
Computed 对象结构:
typescriptinterface ComputedRef<T> extends Ref<T> { readonly effect: ReactiveEffect<T> // ...其他计算属性特有属性 }
-
模板自动解包规则:
-
遇到
{{ variable }}
时:javascriptfunction renderValue(target) { return isRef(target) ? target.value : target }
-
控制台输出详解
1. console.log(firstName)
输出示例:
javascript
RefImpl {
__v_isRef: true,
_value: "ac",
value: "ac" // 通过 getter 访问
}
2. console.log(name)
输出示例:
javascript
ComputedRefImpl {
__v_isRef: true,
_dirty: false,
_value: "acbc", // 缓存的计算结果
effect: ReactiveEffect {...}
}
3. console.log(name.value)
输出结果:
arduino
"acbc" // 直接获取计算值
正确用法总结
脚本区域(<script setup>
)
javascript
// 必须使用 .value 访问
const fullName = firstName.value + lastName.value
console.log(name.value) // 正确
模板区域(<template>
)
vue
<!-- 直接使用变量名 -->
<div>{{ name }}</div>
<!-- 等价于 -->
<div>{{ name.value }}</div> <!-- 错误! -->
常见误区解释
-
为什么模板不能写
.value
?- Vue 的模板编译器已经自动处理解包
- 写
name.value
相当于尝试访问字符串的.value
属性
-
computed
和普通ref
的区别:特性 常规 ref computed 值获取 直接赋值 通过计算函数获取 依赖追踪 无 自动追踪 模板使用 自动解包 自动解包 -
调试建议:
vue<!-- 调试 computed 对象 --> <pre>{{ { computedObject: name } }}</pre>
说人话:
1. ref
和 computed
的本质
- 它们都是 Vue 的**"魔法盒子"**,里面装着值(比如字符串、数字等)
- 但你要从盒子里拿值,在 JS 里 必须用
.value
打开盒子 - 在模板里,Vue 会自动帮你打开盒子,你直接拿就行
2. 你的代码问题
html
<div>{{ name.value }}</div> <!-- 错误! -->
相当于:
- Vue 先自动打开
name
盒子 → 得到字符串"acbc"
- 然后你让 Vue 去拿
"acbc".value
→ 字符串哪有.value
啊!(报错)
3. 正确姿势对比
场景 | JS 代码里 | 模板里 | 原因 |
---|---|---|---|
普通 ref | firstName.value |
{{ firstName }} |
模板自动拆盒子 |
computed | name.value |
{{ name }} |
模板自动拆盒子 |
原始值 | 直接 "ac" |
{{ "ac" }} |
本来就不是盒子 |
4. 通俗比喻
-
JS 环境 :就像你在厨房做菜(需要自己动手开冰箱拿食材)
js// 必须自己开盒子! const food = fridge.value
-
模板环境 :就像你点外卖(商家自动帮你打包好)
html<!-- 直接吃现成的 --> <div>{{ food }}</div>
5. 记住三句话
- 在
<script setup>
里:必须写.value
(手动开盒) - 在
<template>
里:永远不要写.value
(自动拆盒) computed
和ref
在模板里待遇一样,都自动拆
这样写就对了:
vue
<template>
<div>{{ name }}</div> <!-- 显示 "acbc" -->
</template>
<script setup>
const name = computed(() => "ac" + "bc")
console.log(name.value) // 这里必须写 .value
</script>