Vue3 学习教程,从入门到精通,Vue 3 计算属性(Computed Properties)知识点详解与案例代码(15)

Vue 3 计算属性(Computed Properties)知识点详解与案例代码

在 Vue 3 中,计算属性(Computed Properties) 是用于基于响应式数据派生新数据的一种方式。计算属性具有以下特点:

  • 缓存性:只有在依赖的响应式数据发生变化时,计算属性才会重新计算,否则会返回缓存的结果。
  • 可读性:计算属性用于获取数据,而不是修改数据。
  • 响应性:计算属性会自动追踪其依赖的响应式数据,并在依赖变化时自动更新。

一、计算属性的语法知识点

1. 基本语法

在 Vue 3 中,计算属性通常在 setup 函数中使用 computed 函数来定义。

javascript 复制代码
import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const doubleCount = computed(() => count.value * 2);

    return {
      count,
      doubleCount
    };
  }
};

2. 使用 computed 函数

computed 函数接受一个 getter 函数 作为参数,并返回一个 只读的响应式引用(Readonly Ref)

javascript 复制代码
const double = computed(() => count.value * 2);

3. 可写的计算属性

默认情况下,计算属性是只读的。如果需要可写的计算属性,可以传递一个包含 getset 方法的对象。

javascript 复制代码
const firstName = ref('John');
const lastName = ref('Doe');

const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (value) => {
    const parts = value.split(' ');
    firstName.value = parts[0];
    lastName.value = parts[1];
  }
});

4. 依赖追踪

计算属性会自动追踪其内部使用的响应式数据,并在这些数据变化时重新计算。

javascript 复制代码
const firstName = ref('John');
const lastName = ref('Doe');

const fullName = computed(() => `${firstName.value} ${lastName.value}`);

firstNamelastName 变化时,fullName 会自动更新。

5. 懒计算

计算属性只有在首次访问时才会计算,并且只有在依赖变化时才会重新计算。这使得计算属性在需要复杂计算时非常高效。


二、案例代码

以下是一个完整的 Vue 3 单文件组件(Single File Component, SFC)示例,展示了计算属性的使用,包括基本计算属性、可写的计算属性以及依赖追踪。

vue 复制代码
<template>
  <div>
    <h1>Vue 3 计算属性示例</h1>

    <!-- 基本计算属性 -->
    <div>
      <p>当前计数: {{ count }}</p>
      <p>双倍计数: {{ doubleCount }}</p>
      <button @click="increment">增加计数</button>
    </div>

    <!-- 可写的计算属性 -->
    <div>
      <p>全名: {{ fullName }}</p>
      <input v-model="fullName" placeholder="输入全名" />
      <p>名字: {{ firstName }}</p>
      <p>姓氏: {{ lastName }}</p>
    </div>

    <!-- 依赖追踪示例 -->
    <div>
      <p>名字: {{ firstName }}</p>
      <p>姓氏: {{ lastName }}</p>
      <p>全名: {{ fullName }}</p>
      <input v-model="firstName" placeholder="输入名字" />
      <input v-model="lastName" placeholder="输入姓氏" />
    </div>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

export default {
  name: 'ComputedPropertiesExample',
  setup() {
    // 基本计算属性
    const count = ref(0);
    const doubleCount = computed(() => count.value * 2);

    const increment = () => {
      count.value++;
    };

    // 可写的计算属性
    const firstName = ref('John');
    const lastName = ref('Doe');

    const fullName = computed({
      get: () => `${firstName.value} ${lastName.value}`,
      set: (value) => {
        const parts = value.split(' ');
        if (parts.length >= 2) {
          firstName.value = parts[0];
          lastName.value = parts[1];
        } else {
          firstName.value = value;
          lastName.value = '';
        }
      }
    });

    return {
      count,
      doubleCount,
      increment,
      firstName,
      lastName,
      fullName
    };
  }
};
</script>

<style scoped>
h1 {
  color: #42b983;
}

div {
  margin-bottom: 20px;
}

button {
  padding: 5px 10px;
  margin-top: 5px;
}

input {
  padding: 5px;
  margin-top: 5px;
}
</style>

代码详解

  1. 导入 refcomputed

    javascript 复制代码
    import { ref, computed } from 'vue';
    • ref 用于创建响应式数据。
    • computed 用于创建计算属性。
  2. 基本计算属性

    javascript 复制代码
    const count = ref(0);
    const doubleCount = computed(() => count.value * 2);
    • count 是一个响应式的数据。
    • doubleCount 是一个计算属性,其值始终是 count 的两倍。
  3. 增加计数的函数

    javascript 复制代码
    const increment = () => {
      count.value++;
    };
    • increment 函数用于增加 count 的值。
  4. 可写的计算属性

    javascript 复制代码
    const fullName = computed({
      get: () => `${firstName.value} ${lastName.value}`,
      set: (value) => {
        const parts = value.split(' ');
        if (parts.length >= 2) {
          firstName.value = parts[0];
          lastName.value = parts[1];
        } else {
          firstName.value = value;
          lastName.value = '';
        }
      }
    });
    • fullName 是一个可写的计算属性。
    • fullName 被设置时,会自动更新 firstNamelastName
    • firstNamelastName 变化时,fullName 也会自动更新。
  5. 模板部分

    html 复制代码
    <template>
      <div>
        <!-- 基本计算属性 -->
        <div>
          <p>当前计数: {{ count }}</p>
          <p>双倍计数: {{ doubleCount }}</p>
          <button @click="increment">增加计数</button>
        </div>
    
        <!-- 可写的计算属性 -->
        <div>
          <p>全名: {{ fullName }}</p>
          <input v-model="fullName" placeholder="输入全名" />
          <p>名字: {{ firstName }}</p>
          <p>姓氏: {{ lastName }}</p>
        </div>
    
        <!-- 依赖追踪示例 -->
        <div>
          <p>名字: {{ firstName }}</p>
          <p>姓氏: {{ lastName }}</p>
          <p>全名: {{ fullName }}</p>
          <input v-model="firstName" placeholder="输入名字" />
          <input v-model="lastName" placeholder="输入姓氏" />
        </div>
      </div>
    </template>
    • 基本计算属性:展示当前计数和双倍计数,并提供按钮增加计数。
    • 可写的计算属性 :通过 v-model 绑定 fullName,输入全名会自动更新 firstNamelastName
    • 依赖追踪示例 :展示 firstNamelastName,并通过输入框修改它们,fullName 会自动更新。
  6. 样式部分

    css 复制代码
    <style scoped>
    h1 {
      color: #42b983;
    }
    
    div {
      margin-bottom: 20px;
    }
    
    button {
      padding: 5px 10px;
      margin-top: 5px;
    }
    
    input {
      padding: 5px;
      margin-top: 5px;
    }
    </style>
    • 使用 scoped 属性确保样式仅作用于当前组件。
    • 设置了一些基本的样式。

三、运行效果

  1. 基本计算属性

    • 页面加载时,count0doubleCount0
    • 点击"增加计数"按钮,count 增加,doubleCount 自动更新为 count 的两倍。
  2. 可写的计算属性

    • 输入框中输入新的全名,例如 Jane SmithfirstName 会更新为 JanelastName 会更新为 Smith
    • 修改 firstNamelastNamefullName 会自动更新。
  3. 依赖追踪示例

    • 修改 firstNamelastNamefullName 会自动更新。
    • 修改 fullNamefirstNamelastName 会根据输入自动调整。

四、案例代码

vue 复制代码
<template>
  <div class="computed-demo">
    <h2>Vue3 计算属性示例</h2>
    
    <!-- 基础用法展示 -->
    <div class="basic-example">
      <h3>1. 基础只读计算属性</h3>
      <p>姓: <input v-model="firstName" /></p>
      <p>名: <input v-model="lastName" /></p>
      <p>全名: {{ fullName }}</p>
      <p>全名长度: {{ fullNameLength }}</p>
    </div>
    
    <!-- 可写计算属性展示 -->
    <div class="writable-example">
      <h3>2. 可写计算属性</h3>
      <p>输入全名: <input v-model="fullNameWritable" /></p>
      <p>拆分结果: {{ firstName }} {{ lastName }}</p>
    </div>
    
    <!-- 计算属性与方法对比 -->
    <div class="comparison-example">
      <h3>3. 计算属性与方法对比</h3>
      <p>计算属性 (缓存): {{ reversedMessage }} (访问次数: {{ computedAccessCount }})</p>
      <p>方法 (无缓存): {{ reversedMessageMethod() }} (调用次数: {{ methodCallCount }})</p>
      <button @click="incrementCount">点击增加计数</button>
      <p>当前计数: {{ count }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

// 1. 基础示例数据
const firstName = ref('张');
const lastName = ref('三');

// 基础只读计算属性:计算全名
const fullName = computed(() => {
  // 当firstName或lastName变化时,会重新计算
  return `${firstName.value} ${lastName.value}`;
});

// 计算属性可以依赖其他计算属性
const fullNameLength = computed(() => {
  // 依赖fullName计算属性
  return fullName.value.length;
});

// 2. 可写计算属性示例
const fullNameWritable = computed({
  // getter:获取值时调用
  get() {
    return `${firstName.value} ${lastName.value}`;
  },
  // setter:设置值时调用
  set(newValue) {
    // 将输入的全名拆分为姓和名
    const [first, last] = newValue.split(' ');
    firstName.value = first || '';
    lastName.value = last || '';
  }
});

// 3. 计算属性与方法对比示例
const count = ref(0);
const message = ref('Hello Vue3');
const computedAccessCount = ref(0);
const methodCallCount = ref(0);

// 计算属性版本:反转消息
const reversedMessage = computed(() => {
  computedAccessCount.value++;
  return message.value.split('').reverse().join('');
});

// 方法版本:反转消息
function reversedMessageMethod() {
  methodCallCount.value++;
  return message.value.split('').reverse().join('');
}

// 增加计数的方法
function incrementCount() {
  count.value++;
}
</script>

<style scoped>
.computed-demo {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

div > div {
  margin-bottom: 30px;
  padding: 15px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
}

input {
  padding: 6px 10px;
  margin-left: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

button {
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 10px;
}

button:hover {
  background-color: #359e75;
}
</style>

4.1.代码解析

  1. 基础只读计算属性
    • fullName 依赖 firstNamelastName 两个响应式数据
    • 当输入框中的值变化时,fullName 会自动更新
    • fullNameLength 依赖 fullName 这个计算属性,形成计算属性链
  2. 可写计算属性
    • fullNameWritable 同时定义了 getset 方法
    • 当读取时,会调用 get 方法拼接全名
    • 当修改输入框的值时,会调用 set 方法拆分全名到 firstNamelastName
  3. 计算属性与方法对比
    • 点击按钮只会改变 count 的值,不会影响 message
    • 计算属性 reversedMessage 因为依赖没有变化,不会重新计算,访问次数不变
    • 方法 reversedMessageMethod 每次访问都会重新执行,调用次数会增加

五、总结

通过上述知识点和案例代码,可以看出 Vue 3 的计算属性在处理基于响应式数据的派生数据时非常强大且高效。计算属性不仅简化了代码逻辑,还提高了应用的性能和可维护性。在实际开发中,合理使用计算属性可以大大提升开发效率和代码质量。