自学内容网 自学内容网

=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 的值是只读的,不能直接修改。
  • 如果需要可写的计算属性,可以传入 getset 方法。
可写计算属性
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
是否可写默认不可写(可通过 getset 实现)
依赖性独立存在,不依赖其他响应式数据依赖其他响应式数据
性能直接存储值,简单高效有缓存机制,只有依赖数据变化时才重新计算

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() 的功能可以通过 datacomputed 选项实现:

等价代码
<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>

主要转换规则

  1. ref() 转换:

    // Composition API  
    const data = ref([...])  
    
    // Options API  
    data() {  
      return {  
        data: [...]  
      }  
    }  
    
  2. computed() 转换:

    // Composition API  
    const result = computed(() => {...})  
    
    // Options API  
    computed: {  
      result() {  
        return {...}  
      }  
    }  
    
  3. 方法转换:

    // Composition API  
    const updateData = (newData) => {  
      data.value = newData  
    }  
    
    // Options API  
    methods: {  
      updateData(newData) {  
        this.data = newData  
      }  
    }  
    
  4. 数据访问:

    // 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>

注意事项

  1. 性能问题

    • computed() 有缓存机制,只有依赖的数据发生变化时才会重新计算。
    • ref() + watchEffect() 每次依赖变化都会重新执行逻辑,可能会带来性能开销。
  2. 代码简洁性

    • computed() 更加简洁,适合大多数场景。
    • ref() + watchEffect() 需要手动更新值,代码稍显冗长。
  3. 推荐使用场景

    • 如果你的逻辑依赖多个响应式数据,并且需要缓存,优先使用 computed()
    • 如果你的逻辑非常简单,或者不需要缓存,可以使用 ref() + watchEffect()

总结

  • computed() 是首选:它更简洁、性能更好,适合大多数场景。
  • ref() 替代 computed():可以通过 watchEffect() 手动实现,但代码复杂度会增加,且没有缓存机制。

参考:

=ref() =computed()


原文地址:https://blog.csdn.net/qq_37757277/article/details/144012550

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!