=computed() =ref()
=computed() =ref()
在 Vue 中,computed()
和 ref()
是 Vue 3 组合式 API 的核心工具,它们分别用于 计算属性 和 响应式数据。以下是它们的区别和用法:
1. ref()
作用
- 用于创建响应式的单一数据。
- 可以是基本类型(如字符串、数字、布尔值)或对象类型。
- 当
ref
的值发生变化时,依赖它的组件会自动重新渲染。
用法
import { ref } from 'vue';
const count = ref(0); // 创建一个响应式变量
// 修改值
count.value++; // 必须通过 .value 修改或访问 ref 的值
console.log(count.value); // 输出: 1
特点
- 基本类型:
ref
会将值包装为一个响应式对象,访问或修改时需要通过.value
。 - 对象类型:如果
ref
包裹的是对象,Vue 会自动将对象的属性变为响应式。
示例
import { ref } from 'vue';
const user = ref({ name: 'Alice', age: 25 }); // 包裹一个对象
// 修改对象属性
user.value.name = 'Bob';
console.log(user.value.name); // 输出: Bob
2. computed()
作用
- 用于定义基于其他响应式数据的 派生状态。
computed
的值会根据依赖的响应式数据自动更新。- 适合用来处理需要动态计算的值。
用法
import { ref, computed } from 'vue';
const count = ref(0);
// 定义一个计算属性
const doubleCount = computed(() => count.value * 2);
console.log(doubleCount.value); // 输出: 0
count.value++; // 修改 count 的值
console.log(doubleCount.value); // 输出: 2
特点
computed
的值是只读的,不能直接修改。- 如果需要可写的计算属性,可以传入
get
和set
方法。
可写计算属性
import { ref, computed } from 'vue';
const count = ref(0);
// 可写计算属性
const doubleCount = computed({
get: () => count.value * 2,
set: (newValue) => {
count.value = newValue / 2; // 反向修改 count
}
});
doubleCount.value = 10; // 修改 doubleCount
console.log(count.value); // 输出: 5
3. ref()
和 computed()
的区别
特性 | ref() | computed() |
---|---|---|
用途 | 定义响应式的单一数据 | 定义基于其他响应式数据的派生状态 |
是否需要 .value | 是 | 是 |
是否可写 | 是 | 默认不可写(可通过 get 和 set 实现) |
依赖性 | 独立存在,不依赖其他响应式数据 | 依赖其他响应式数据 |
性能 | 直接存储值,简单高效 | 有缓存机制,只有依赖数据变化时才重新计算 |
4. 综合示例
以下是一个同时使用 ref()
和 computed()
的示例:
<script setup>
import { ref, computed } from 'vue';
// 定义响应式数据
const price = ref(100);
const quantity = ref(2);
// 定义计算属性
const total = computed(() => price.value * quantity.value);
// 修改响应式数据
price.value = 150;
console.log(total.value); // 输出: 300
</script>
<template>
<div>
<p>单价: {{ price }}</p>
<p>数量: {{ quantity }}</p>
<p>总价: {{ total }}</p>
</div>
</template>
5. 在 Options API 中的等价用法
在 Vue 3 的 Options API 中,ref()
和 computed()
的功能可以通过 data
和 computed
选项实现:
等价代码
<script>
export default {
data() {
return {
price: 100, // 等价于 ref(100)
quantity: 2 // 等价于 ref(2)
};
},
computed: {
total() {
return this.price * this.quantity; // 等价于 computed(() => price.value * quantity.value)
}
}
};
</script>
<template>
<div>
<p>单价: {{ price }}</p>
<p>数量: {{ quantity }}</p>
<p>总价: {{ total }}</p>
</div>
</template>
6. 总结
ref()
:用于定义响应式的单一数据,适合存储基本类型或对象。computed()
:用于定义基于其他响应式数据的派生状态,具有缓存机制。- 组合使用:
ref()
定义基础数据,computed()
定义基于基础数据的动态值。
case 2
<script setup>
import { ref, computed } from 'vue';
// 原始数据
const data = ref([
{ position: { x: 1, y: 2 } },
{ position: { x: 3, y: 4 } },
{ position: { x: 5, y: 6 } }
]);
// 数据转换函数
const convertData = (sourceData) => {
try {
const paths = sourceData
.filter(item => item?.position && typeof item.position === 'object')
.map(item => item.position);
return [{
paths: paths
}];
} catch (error) {
console.error('数据转换错误:', error);
return [{ paths: [] }];
}
};
// 计算属性
const convertTodata = computed(() => convertData(data.value));
// 更新数据的方法
const updateData = (newData) => {
data.value = newData;
};
</script>
<template>
<div class="p-4">
<h2 class="text-xl mb-4">转换后的数据:</h2>
<pre class="bg-gray-100 p-4 rounded">
{{ JSON.stringify(convertTodata, null, 2) }} // 使用 JSON.stringify 将 convertTodata 的结果格式化为 JSON 字符串并显示在页面
</pre>
</div>
</template>
<script>
export default {
name: 'DataConverter',
// ref() -> data()
data() {
return {
// const data = ref([...]) 变成:
data: [
{ position: { x: 1, y: 2 } },
{ position: { x: 3, y: 4 } },
{ position: { x: 5, y: 6 } }
]
}
},
// computed() -> computed: {}
computed: {
// const convertTodata = computed(() => ...) 变成:
convertTodata() {
return this.convertData(this.data);
}
},
methods: {
convertData(sourceData) {
try {
const paths = sourceData
.filter(item => item?.position && typeof item.position === 'object')
.map(item => item.position);
return [{
paths: paths
}];
} catch (error) {
console.error('数据转换错误:', error);
return [{ paths: [] }];
}
},
// const updateData = (newData) => { data.value = newData } 变成:
updateData(newData) {
this.data = newData;
}
}
};
</script>
<template>
<div class="p-4">
<h2 class="text-xl mb-4">转换后的数据:</h2>
<pre class="bg-gray-100 p-4 rounded">
{{ JSON.stringify(convertTodata, null, 2) }}
</pre>
</div>
</template>
<style scoped>
.p-4 {
padding: 1rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.bg-gray-100 {
background-color: #f3f4f6;
}
.rounded {
border-radius: 0.25rem;
}
</style>
主要转换规则
-
ref() 转换:
// Composition API const data = ref([...]) // Options API data() { return { data: [...] } }
-
computed() 转换:
// Composition API const result = computed(() => {...}) // Options API computed: { result() { return {...} } }
-
方法转换:
// Composition API const updateData = (newData) => { data.value = newData } // Options API methods: { updateData(newData) { this.data = newData } }
-
数据访问:
// Composition API data.value // Options API this.data
=computed() 能改为 =ref() 吗
在 Vue 3 中,computed()
和 ref()
是不同的工具,虽然它们都可以创建响应式数据,但它们的用途和行为不同。computed()
不建议直接改为 ref()
,因为它们的核心区别在于:
ref()
是用于存储和管理响应式的单一数据。computed()
是用于基于其他响应式数据派生出新的值,并且具有 缓存机制。
如果你想用 ref()
替代 computed()
,需要手动实现类似 computed()
的功能,但这通常不推荐,因为 computed()
已经为你处理了依赖追踪和缓存。=ref()
会丢失 computed()
的依赖追踪和缓存特性。
什么时候可以用 ref()
替代 computed()
?
如果你的计算逻辑非常简单,且不需要依赖缓存机制,可以用 ref()
和 watchEffect()
或 watch()
来实现类似的功能。
如何用 ref()
替代 computed()
?
以下是一个示例,展示如何用 ref()
和 watchEffect()
替代 computed()
:
原始代码:使用 computed()
import { ref, computed } from 'vue';
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
console.log(doubleCount.value); // 输出: 0
count.value++;
console.log(doubleCount.value); // 输出: 2
改为使用 ref()
和 watchEffect()
import { ref, watchEffect } from 'vue';
const count = ref(0);
const doubleCount = ref(0);
// 使用 watchEffect 手动更新 doubleCount
watchEffect(() => {
doubleCount.value = count.value * 2;
});
console.log(doubleCount.value); // 输出: 0
count.value++;
console.log(doubleCount.value); // 输出: 2
对比分析
特性 | computed() | ref() + watchEffect() | watch |
---|---|---|---|
依赖追踪 | 自动追踪依赖 | 否, 使用 watchEffect 手动更新值 | 否, 使用 watch 手动更新值 |
缓存机制 | 有缓存,每次使用时使用的预先计算存储的的 cache,依赖变化时重新计算, 使用时使用的重新计算存储的的 new cache | 没有缓存,每次使用时重新计算,依赖变化时重新计算 | 否, 每次 data(){} 变化都会重新执行 |
代码复杂度 | 简洁,直接定义计算逻辑 | 需要手动更新值,代码稍显冗长 | |
适用场景 | 适合派生状态,依赖多个响应式数据 | 适合简单逻辑或不需要缓存的场景 |
完整示例:从 computed()
改为 ref()
假设你有一个组件,使用 computed()
来计算数据:
原始代码:使用 computed()
<script setup>
import { ref, computed } from 'vue';
const price = ref(100);
const quantity = ref(2);
// 计算总价
const total = computed(() => price.value * quantity.value);
</script>
<template>
<div>
<p>单价: {{ price }}</p>
<p>数量: {{ quantity }}</p>
<p>总价: {{ total }}</p>
</div>
</template>
改为使用 ref()
和 watchEffect()
<script setup>
import { ref, watchEffect } from 'vue';
const price = ref(100);
const quantity = ref(2);
// 使用 ref 存储总价
const total = ref(0);
// 手动更新 total 的值
watchEffect(() => {
total.value = price.value * quantity.value;
});
</script>
<template>
<div>
<p>单价: {{ price }}</p>
<p>数量: {{ quantity }}</p>
<p>总价: {{ total }}</p>
</div>
</template>
原始代码:使用 computed()
<script>
export default {
name: 'DataConverter',
// ref() -> data()
data() {
return {
// const data = ref([...]) 变成:
data: [
{ position: { x: 1, y: 2 } },
{ position: { x: 3, y: 4 } },
{ position: { x: 5, y: 6 } }
]
}
},
// computed() -> computed: {}
computed: {
// const convertTodata = computed(() => ...) 变成:
convertTodata() {
return this.convertData(this.data);
}
},
methods: {
convertData(sourceData) {
try {
const paths = sourceData
.filter(item => item?.position && typeof item.position === 'object')
.map(item => item.position);
return [{
paths: paths
}];
} catch (error) {
console.error('数据转换错误:', error);
return [{ paths: [] }];
}
},
// const updateData = (newData) => { data.value = newData } 变成:
updateData(newData) {
this.data = newData;
}
}
};
</script>
<template>
<div class="p-4">
<h2 class="text-xl mb-4">转换后的数据:</h2>
<pre class="bg-gray-100 p-4 rounded">
{{ JSON.stringify(convertTodata, null, 2) }}
</pre>
</div>
</template>
改为使用 ref()
和 watchEffect()
<template>
<div class="p-4">
<h2 class="text-xl mb-4">转换后的数据:</h2>
<pre class="bg-gray-100 p-4 rounded">
{{ JSON.stringify(convertTodata, null, 2) }}
</pre>
</div>
</template>
<script>
export default {
data() {
return {
// 原始数据
data1: [
{ position: { x: 1, y: 2 } },
{ position: { x: 3, y: 4 } },
{ position: { x: 5, y: 6 } }
],
irrelevantData: 'something', // 无关数据
// 将 computed 改为 data 中的属性
// 移除了 computed 属性:原来的计算属性被转换为 data 中的普通响应式数据
// 无自动追踪依赖,需watch 无缓存机制 不是只有依赖变化时( data1.value 变化时)才重新计算,每次 data ( 指总的 data(){},例如 irrelevantData change ) 变化都会执行
convertTodata: [{ paths: [] }],
}
},
// 监听 data ( 指总的 data(){},例如 irrelevantData change )的变化,更新 convertTodata
watch: {
data: { // 指总的 data(){}
immediate: true, // 确保初始化时执行一次,立即执行一次
handler(newData) {
// 当 data 变化时更新 convertTodata
this.convertTodata = this.convertData(newData);
}
}
},
methods: {
convertData(sourceData) {
try {
const paths = sourceData
.filter(item => item?.position && typeof item.position === 'object')
.map(item => item.position);
return [{
paths: paths
}];
} catch (error) {
console.error('数据转换错误:', error);
return [{ paths: [] }];
}
},
updateData(newData) {
this.data = newData;
}
}
}
</script>
注意事项
-
性能问题:
computed()
有缓存机制,只有依赖的数据发生变化时才会重新计算。ref()
+watchEffect()
每次依赖变化都会重新执行逻辑,可能会带来性能开销。
-
代码简洁性:
computed()
更加简洁,适合大多数场景。ref()
+watchEffect()
需要手动更新值,代码稍显冗长。
-
推荐使用场景:
- 如果你的逻辑依赖多个响应式数据,并且需要缓存,优先使用
computed()
。 - 如果你的逻辑非常简单,或者不需要缓存,可以使用
ref()
+watchEffect()
。
- 如果你的逻辑依赖多个响应式数据,并且需要缓存,优先使用
总结
computed()
是首选:它更简洁、性能更好,适合大多数场景。ref()
替代computed()
:可以通过watchEffect()
手动实现,但代码复杂度会增加,且没有缓存机制。
参考:
原文地址:https://blog.csdn.net/qq_37757277/article/details/144012550
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!