Vue.js 从新手到专家:第五章 Composition API

欢迎来到《Vue.js 从新手到专家》的第五章!在这一章中,我们将深入探讨 Vue 3 的核心特性之一:Composition API。与 Options API 不同,Composition API 提供了一种更灵活、更强大的方式来组织和复用组件逻辑。

通过学习本章内容,你将掌握以下技能:

  1. 理解 Composition API 的基本概念及其优势。
  2. 使用 ref()reactive() 处理数据。
  3. 掌握生命周期钩子的使用方法。
  4. 学习如何通过 computed() 创建计算属性。
  5. 创建可重用的组合式函数(Composable Functions)。
  6. 完成实践挑战,巩固所学知识。

让我们开始吧!


1 使用 Composition API 设置组件

1.1 Composition API 的基本结构

Composition API 的核心思想是将组件的逻辑按功能拆分,而不是像 Options API 那样按选项组织。这种方式特别适合处理复杂的业务逻辑。

示例:一个简单的计数器组件
<!-- Counter.vue -->
<template>
  <div>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">增加</button>
    <button @click="reset">重置</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    // 定义响应式数据
    const count = ref(0);

    // 定义方法
    const increment = () => {
      count.value++;
      console.log("计数增加!");
    };

    const reset = () => {
      count.value = 0;
      console.log("计数已重置!");
    };

    // 返回需要暴露的内容
    return {
      count,
      increment,
      reset
    };
  }
});
</script>
输出结果:
  1. 页面加载时,显示“当前计数:0”。
  2. 点击“增加”按钮后,计数递增,并在控制台输出“计数增加!”。
  3. 点击“重置”按钮后,计数恢复为 0,并在控制台输出“计数已重置!”。
小贴士:
  • setup() 是 Composition API 的入口函数,所有逻辑都在其中定义。
  • setup() 中定义的数据和方法需要通过 return 暴露给模板。

1.2 Composition API 的优势

  1. 逻辑复用:通过组合式函数(Composables),可以轻松实现逻辑复用。
  2. 代码组织:将相关逻辑集中在一起,便于理解和维护。
  3. TypeScript 支持:提供更好的类型推断和代码补全。

2 使用 ref() 和 reactive() 处理数据

Vue 3 提供了两种创建响应式数据的方式:ref()reactive()

2.1 使用 ref()

ref() 用于创建基本类型的响应式数据。

示例:使用 ref() 创建响应式变量
<!-- RefExample.vue -->
<template>
  <div>
    <p>消息:{{ message }}</p>
    <button @click="updateMessage">更新消息</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    // 创建响应式变量
    const message = ref("初始消息");

    // 更新变量值
    const updateMessage = () => {
      message.value = "消息已更新";
    };

    return {
      message,
      updateMessage
    };
  }
});
</script>
输出结果:
  1. 页面加载时,显示“消息:初始消息”。
  2. 点击“更新消息”按钮后,消息变为“消息已更新”。
小贴士:
  • 访问或修改 ref 的值时,需要使用 .value 属性。
  • ref 适用于基本类型(如字符串、数字、布尔值)。

2.2 使用 reactive()

reactive() 用于创建对象类型的响应式数据。

示例:使用 reactive() 创建响应式对象
<!-- ReactiveExample.vue -->
<template>
  <div>
    <p>用户名:{{ user.name }}</p>
    <p>年龄:{{ user.age }}</p>
    <button @click="updateUser">更新用户信息</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from "vue";

export default defineComponent({
  setup() {
    // 创建响应式对象
    const user = reactive({
      name: "张三",
      age: 25
    });

    // 更新对象属性
    const updateUser = () => {
      user.name = "李四";
      user.age = 30;
    };

    return {
      user,
      updateUser
    };
  }
});
</script>
输出结果:
  1. 页面加载时,显示“用户名:张三”和“年龄:25”。
  2. 点击“更新用户信息”按钮后,用户名变为“李四”,年龄变为“30”。
小贴士:
  • reactive 返回的是一个代理对象,直接访问或修改其属性即可触发响应式更新。
  • 对于嵌套对象,reactive 会自动递归地将其内部属性转换为响应式。

2.3 ref()reactive() 的选择

特性ref()reactive()
数据类型基本类型对象类型
访问方式需要通过 .value 访问直接访问属性
性能轻量级对复杂对象更高效

3 使用生命周期钩子

在 Composition API 中,生命周期钩子以函数的形式提供,例如 onMountedonUnmounted 等。

3.1 示例:使用生命周期钩子

<!-- LifecycleHooks.vue -->
<template>
  <div>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted } from "vue";

export default defineComponent({
  setup() {
    const count = ref(0);

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

    // 组件挂载时执行
    onMounted(() => {
      console.log("组件已挂载!");
    });

    // 组件卸载时执行
    onUnmounted(() => {
      console.log("组件已卸载!");
    });

    return {
      count,
      increment
    };
  }
});
</script>
输出结果:
  1. 当组件加载时,控制台输出“组件已挂载!”。
  2. 当组件卸载时,控制台输出“组件已卸载!”。
小贴士:
  • 使用 onMountedonUnmounted 可以在组件生命周期的不同阶段执行逻辑。
  • 如果需要清理资源(如定时器或事件监听器),建议在 onUnmounted 中进行。

4 了解 Composition API 中的观察者

观察者(Watchers)用于监听响应式数据的变化,并在变化时执行回调函数。

4.1 示例:使用 watch()

<!-- WatchExample.vue -->
<template>
  <div>
    <input v-model="message" placeholder="请输入内容" />
    <p>消息长度:{{ messageLength }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, computed } from "vue";

export default defineComponent({
  setup() {
    const message = ref("");

    // 计算属性
    const messageLength = computed(() => message.value.length);

    // 观察 message 的变化
    watch(
      message,
      (newVal, oldVal) => {
        console.log(`消息从 "${oldVal}" 变为 "${newVal}"`);
      },
      { immediate: true } // 立即执行一次回调
    );

    return {
      message,
      messageLength
    };
  }
});
</script>
输出结果:
  1. 用户输入“Hello”时,页面显示“消息长度:5”。
  2. 控制台输出:
  • 消息从 "" 变为 "H"
    消息从 "H" 变为 "He"
    消息从 "He" 变为 "Hel"
    消息从 "Hel" 变为 "Hell"
    消息从 "Hell" 变为 "Hello"
小贴士:
  • watch 的回调函数接收两个参数:新值和旧值。
  • 使用 { immediate: true } 可以在初始化时立即执行回调。

4.2 示例:使用 watchEffect()

watchEffect 是一种更简洁的观察方式,它会在依赖发生变化时自动重新执行。

<!-- WatchEffectExample.vue -->
<template>
  <div>
    <input v-model="message" placeholder="请输入内容" />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watchEffect } from "vue";

export default defineComponent({
  setup() {
    const message = ref("");

    // 自动观察 message 的变化
    watchEffect(() => {
      console.log("当前消息:", message.value);
    });

    return {
      message
    };
  }
});
</script>
输出结果:

每当用户输入内容时,控制台都会输出当前消息。

小贴士:
  • watchEffect 不需要显式指定依赖,它会自动追踪所有引用的响应式数据。
  • 如果需要手动控制依赖,建议使用 watch

5 使用 computed()

计算属性(Computed Properties)允许基于其他响应式数据动态计算值。

5.1 示例:使用 computed()

<!-- ComputedExample.vue -->
<template>
  <div>
    <input v-model="firstName" placeholder="请输入名字" />
    <input v-model="lastName" placeholder="请输入姓氏" />
    <p>完整姓名:{{ fullName }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed } from "vue";

export default defineComponent({
  setup() {
    const firstName = ref("");
    const lastName = ref("");

    // 计算属性
    const fullName = computed(() => `${firstName.value} ${lastName.value}`);

    return {
      firstName,
      lastName,
      fullName
    };
  }
});
</script>
输出结果:

假设用户输入名字为“张”,姓氏为“三”,页面将显示:

完整姓名:张 三
小贴士:
  • 计算属性是基于依赖自动更新的,因此比手动方法更高效。
  • 如果需要双向绑定计算属性,可以使用 getter 和 setter。

6 创建可重用的可组合钩子

组合式函数(Composables)是一组封装了特定逻辑的函数,可以在多个组件中复用。

6.1 示例:创建一个计数器组合式函数

// useCounter.ts
import { ref } from "vue";

export function useCounter() {
  const count = ref(0);

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

  const decrement = () => {
    count.value--;
  };

  const reset = () => {
    count.value = 0;
  };

  return { count, increment, decrement, reset };
}

vue

<!-- Counter.vue -->
<template>
  <div>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">增加</button>
    <button @click="decrement">减少</button>
    <button @click="reset">重置</button>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { useCounter } from "./useCounter";

export default defineComponent({
  setup() {
    const { count, increment, decrement, reset } = useCounter();

    return {
      count,
      increment,
      decrement,
      reset
    };
  }
});
</script>
输出结果:
  1. 页面加载时,显示“当前计数:0”。
  2. 点击“增加”按钮后,计数递增。
  3. 点击“减少”按钮后,计数递减。
  4. 点击“重置”按钮后,计数恢复为 0。
小贴士:
  • 组合式函数可以封装复杂的逻辑,提升代码的复用性和可维护性。
  • 使用 TypeScript 时,建议为组合式函数添加类型声明。

7 实践挑战

为了巩固所学知识,请尝试完成以下挑战:

7.1 挑战:构建一个任务管理应用

要求:

  1. 创建一个名为 TaskList 的组件,显示任务列表。
  2. 使用 ref()reactive() 管理任务数据。
  3. 使用 computed() 创建过滤后的任务列表(如仅显示未完成的任务)。
  4. 使用组合式函数封装任务管理逻辑。
示例代码:
// useTasks.ts
import { ref } from "vue";

export function useTasks() {
  const tasks = ref([
    { id: 1, text: "任务 A", completed: false },
    { id: 2, text: "任务 B", completed: true }
  ]);

  const addTask = (text: string) => {
    tasks.value.push({ id: Date.now(), text, completed: false });
  };

  const toggleTask = (id: number) => {
    const task = tasks.value.find((t) => t.id === id);
    if (task) {
      task.completed = !task.completed;
    }
  };

  const deleteTask = (id: number) => {
    tasks.value = tasks.value.filter((t) => t.id !== id);
  };

  return { tasks, addTask, toggleTask, deleteTask };
}

vue

<!-- TaskList.vue -->
<template>
  <div>
    <h2>任务列表</h2>
    <ul>
      <li v-for="task in filteredTasks" :key="task.id">
        <input type="checkbox" :checked="task.completed" @change="toggleTask(task.id)" />
        <span :class="{ completed: task.completed }">{{ task.text }}</span>
        <button @click="deleteTask(task.id)">删除</button>
      </li>
    </ul>
    <input v-model="newTaskText" placeholder="请输入新任务" />
    <button @click="addTask">添加任务</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed } from "vue";
import { useTasks } from "./useTasks";

export default defineComponent({
  setup() {
    const { tasks, addTask, toggleTask, deleteTask } = useTasks();

    const newTaskText = ref("");

    const filteredTasks = computed(() => tasks.value.filter((t) => !t.completed));

    const handleAddTask = () => {
      if (newTaskText.value.trim() !== "") {
        addTask(newTaskText.value);
        newTaskText.value = "";
      }
    };

    return {
      tasks,
      filteredTasks,
      newTaskText,
      addTask: handleAddTask,
      toggleTask,
      deleteTask
    };
  }
});
</script>

<style scoped>
.completed {
  text-decoration: line-through;
  color: gray;
}
</style>
输出结果:
  1. 页面加载时,显示任务列表。
  2. 添加新任务后,任务被添加到列表中。
  3. 勾选任务前的复选框,任务文字变为划线效果。
  4. 点击“删除”按钮后,任务从列表中移除。

8 总结

本章详细介绍了 Vue 3 的 Composition API,包括 ref()reactive() 的使用、生命周期钩子、观察者、计算属性以及组合式函数的创建。通过这些知识点的学习,你已经掌握了如何使用 Composition API 构建高效且可维护的 Vue 应用。

在接下来的章节中,我们将继续探索 Vue.js 的高级特性和最佳实践,帮助你成为一名专业的 Vue.js 开发者!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

caifox菜狐狸

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值