青少年编程与数学 02-006 前端开发框架VUE 04课题、组合式API

青少年编程与数学 02-006 前端开发框架VUE 04课题、组合式API

课题摘要:本文介绍了Vue 3的组合式API,它通过setup函数组织组件逻辑,提升逻辑复用性和可维护性。组合式API支持响应式状态管理、生命周期钩子、依赖注入,使代码结构更清晰,易于维护。文章详细解释了setup函数、props对象、context对象的用法,以及如何管理响应式状态、创建计算属性和侦听器。此外,还探讨了Vue 3的响应式基础、refreactive函数的使用方法,以及如何利用provideinject实现跨组件通信。最后,通过一个Todo应用示例,展示了组合式API在实际项目中的应用。


本课程目的是概要地了解vue中的组合式API,详细内容后面将分解学习。

一、组合式 API

Vue 3 引入了组合式 API(Composition API),这是一种新的 API 风格,旨在提升组件的逻辑复用性和可维护性。以下是组合式 API 的一些关键特点和概念:

1. 函数式组织

组合式 API 主要通过 setup 函数来组织组件逻辑。所有的组合式 API 都在 setup 函数中使用,该函数在组件实例创建之前调用。

2. 响应式状态

通过 refreactive 创建响应式状态,使得组件的数据能够自动更新视图。

3. 生命周期钩子

使用 onMountedonUpdated 等函数来管理生命周期,替代了 Vue 2 中的 mountedupdated 等选项。

4. 依赖注入

通过 provideinject 来实现依赖注入,使得组件之间可以共享数据和方法。

5. 逻辑复用性高

组合式 API 允许开发者将相关的逻辑代码(如数据获取、状态管理、事件处理等)组合在一起,形成一个个独立的函数,这些函数可以在多个组件中复用。

6. 代码可读性好

在处理复杂组件逻辑时,组合式 API 可以让代码结构更清晰。将不同功能的逻辑(如数据监听、计算属性等)分别放在不同的函数或代码块中,而不是像选项式 API 那样分散在 datamethodscomputed 等选项中,这样更便于理解和维护组件的功能。

7. 更好的类型推断

对于使用 TypeScript 的项目,组合式 API 提供了更好的类型推断支持。开发者可以更方便地为函数的参数和返回值定义类型,从而减少类型错误。

8. 常用的组合式 API 方法

  • ref:用于创建响应式的基本类型数据。
  • computed:用于创建计算属性,依赖于其他响应式数据,并且只有在依赖发生变化时才会重新计算值。
  • watchwatchEffect:用于侦听数据的变化并作出响应。
  • 生命周期钩子:如 onBeforeMountonMounted 等,用于替代 Options API 中的生命周期钩子。

9. 跨组件通信

Vue 3 提供了多种方式来实现跨组件通信,包括 propsemitprovide/inject 等。

组合式 API 的引入,使得 Vue 3 的组件编写更加灵活和模块化,特别适合处理复杂的逻辑和状态管理,同时也使得代码更加易于维护和理解。

二、setup 函数

Vue 3 的组合式 API 中的 setup 函数是一个非常重要的概念,它是组件的入口点,允许开发者以一种更灵活和组织化的方式编写组件逻辑。以下是 setup 函数的详细解释:

定义和作用

  • setup 函数是 Vue 3 中引入的一个新的组件选项,它是响应式组件的入口。
  • 它在组件实例创建之前执行,并且只能在定义组件时使用。
  • setup 函数提供了一个上下文对象,其中包含了组件的属性、插槽、属性和上下文(emit, attrs, slots, context)。

函数签名

setup 函数可以接收两个参数:

  1. props:一个包含所有传入组件的属性的对象。
  2. context:一个对象,包含了以下属性:
    • attrs:包含所有未在 props 中声明的属性绑定(v-bind)。
    • slots:包含所有插槽。
    • emit:用于触发事件的函数。
    • expose:用于暴露公共实例属性的函数。
    • root:指向根实例的引用。

返回值

  • setup 函数可以返回任何类型的值,这些值将在模板或其他组件选项中使用。
  • 返回的基本类型值(如字符串、数字等)将被转换为一个响应式的 ref 对象。
  • 返回的对象将自动转换为 setup 函数的响应式属性。

生命周期

  • setup 函数在组件实例化之前执行,并且只在组件的整个生命周期中执行一次。
  • 它在 beforeCreate 钩子之前执行,因此此时组件的 datacomputedmethods 还未被初始化。

使用场景

  • 管理响应式状态:使用 refreactive 创建响应式数据。
  • 计算属性:使用 computed 创建依赖于响应式数据的计算属性。
  • 侦听器:使用 watchwatchEffect 侦听响应式数据的变化。
  • 生命周期钩子:使用 onMountedonUpdated 等生命周期函数。
  • 依赖注入:使用 provideinject 实现跨组件的状态共享。

示例

下面是一个简单的 setup 函数示例:

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

export default {
  setup(props, { attrs, slots, emit }) {
    // 创建响应式数据
    const count = ref(0);

    // 创建计算属性
    const doubledCount = computed(() => count.value * 2);

    // 创建侦听器
    watch(count, (newValue, oldValue) => {
      console.log(`count changed from ${oldValue} to ${newValue}`);
    });

    // 使用生命周期钩子
    onMounted(() => {
      console.log('Component is mounted');
    });

    // 可以返回任何值,这些值将在模板中可用
    return {
      count,
      doubledCount
    };
  }
};

在这个示例中,setup 函数创建了一个响应式数据 count,一个计算属性 doubledCount,并设置了一个侦听器来监听 count 的变化。同时,它还使用了一个生命周期钩子 onMounted 来在组件挂载后执行操作。最后,它返回了 countdoubledCount,这些值可以在组件的模板中使用。

setup 函数是组合式 API 的核心,它提供了一种更灵活和组织化的方式来编写组件逻辑,使得代码更加模块化和可维护。

三、 props 对象

在 Vue 3 的组合式 API 中,props 对象并不是一个 API 或函数,而是组件的一个选项,它用于定义组件接受的外部传入的数据。在使用组合式 API 时,props 的处理方式与传统的选项式 API(Options API)有所不同。

定义 Props

在使用组合式 API 时,你仍然可以在组件中定义 props,但是访问这些 props 的方式有所变化。在 setup 函数中,props 作为第一个参数传入,你可以直接使用这个参数。

javascript 复制代码
import { defineComponent } from 'vue';

export default defineComponent({
  props: {
    message: String
  },
  setup(props) {
    // 在这里,props 是一个对象,包含了所有传入的 props
    console.log(props.message); // 访问传入的 props

    // 你可以返回 props,使其在模板或其他组合式 API 中可用
    return {
      message: props.message
    };
  }
});

访问 Props

setup 函数中,props 对象包含了所有父组件传递给子组件的数据。你可以直接访问这些数据,就像访问普通 JavaScript 对象的属性一样。

使用 toRefs 转换 Props

由于 props 对象中的属性不是响应式的,如果你需要将 props 对象中的属性作为响应式引用(ref)使用,可以使用 toRefs 函数将 props 对象转换为一个响应式的对象,其中每个属性都是一个 ref

javascript 复制代码
import { toRefs } from 'vue';

export default {
  setup(props) {
    // 将 props 对象转换为响应式引用
    const propsRefs = toRefs(props);

    // 现在可以使用 propsRefs.message 作为响应式引用
    return {
      ...propsRefs
    };
  }
};

注意事项

  1. 响应式 :在 setup 函数中直接使用 props 对象时,它们不是响应式的。如果你需要响应式引用,使用 toRefs 进行转换。

  2. 不可变props 应该是不可变的,这意味着你不应该在子组件内部修改 props 的值。

  3. 类型检查和验证 :你可以在 props 定义中指定类型和验证规则,Vue 会在开发模式下进行类型检查和验证。

  4. 与模板的集成 :在模板中使用 props 时,不需要任何特殊处理,直接使用即可。

通过以上方式,你可以在 Vue 3 的组合式 API 中有效地使用和管理 props,使得组件之间的数据传递更加清晰和高效。

四、context 对象

在 Vue 3 的组合式 API 中,context 对象是一个非常有用的工具,它允许我们在 setup 函数中访问组件的上下文信息,包括组件的属性、事件、插槽等。以下是 context 对象的详细解释和使用指南:

什么是 context 对象

context 对象提供了对组件上下文的访问,包括但不限于 attrsslotsemit 等属性和方法。这些属性和方法允许我们直接在 setup 函数中访问组件的非响应式属性、插槽和事件发射器,而不需要使用 this 关键字。

context 对象的属性和方法

  1. attrs :包含了所有未被 props 属性消耗的属性绑定(即 v-bind 绑定的属性),类似于 Options API 中的 this.$attrs

  2. slots :包含了组件的所有插槽,类似于 Options API 中的 this.$slots

  3. emit :一个函数,用于触发父组件的事件,类似于 Options API 中的 this.$emit

  4. expose:一个函数,用于暴露给父组件的响应式属性或方法,这样父组件就可以访问到子组件内部的响应式状态或方法。

如何使用 context 对象

setup 函数中,context 作为第二个参数传入,可以直接调用 context() 来获取 context 对象:

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

export default {
  setup(props, context) {
    const count = ref(0);
    const emit = context.emit; // 获取 emit 方法
    const increment = () => {
      count.value++;
      emit('increment'); // 触发事件
    };

    return {
      count,
      increment
    };
  }
};

context 对象的优势

  1. 简化代码context 函数提供了一种更简洁的方式来访问组件的上下文信息,减少了在组件中使用 this 的次数。

  2. 易于使用context 函数使用起来非常简单,只需调用 context 函数即可获取组件的上下文信息。

  3. 类型安全 :通过 TypeScript,可以为 context 函数添加类型注解,提高代码的类型安全性。

注意事项

  1. 避免过度使用 :虽然 context 非常有用,但过度使用可能会导致代码难以理解。应根据实际需求合理使用。

  2. 遵循最佳实践 :在编写 Vue 3 代码时,遵循最佳实践,如使用 context 函数等。

总结来说,context 对象是 Vue 3 组合式 API 中的一个核心功能,它使得我们可以更方便地访问组件的上下文信息,从而编写出更清晰、更易于维护的代码。

五、管理响应式状态

在 Vue 3 中,组合式 API 提供了 refreactive 两个函数来创建和管理响应式状态。以下是如何使用这两个函数的详细说明:

使用 ref 创建响应式基本类型数据

ref 函数用于创建一个响应式的引用(reference),它包装了一个值,使得这个值在 Vue 的响应式系统中是可跟踪的。这对于基本类型的数据(如字符串、数字、布尔值)特别有用。

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

const count = ref(0); // 创建一个响应式的数字

// 访问和修改 ref 的值需要使用 .value 属性
console.log(count.value); // 输出 0
count.value++; // 增加计数

在模板中使用 ref

vue 复制代码
<template>
  <div>{{ count }}</div>
</template>

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

const count = ref(0);
</script>

使用 reactive 创建响应式对象

reactive 函数用于创建一个响应式对象,它使得对象内的所有属性都是响应式的。

javascript 复制代码
import { reactive } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue'
});

// 直接修改对象的属性即可
console.log(state.count); // 输出 0
state.count++; // 增加计数

在模板中使用 reactive

vue 复制代码
<template>
  <div>{{ state.count }} - {{ state.name }}</div>
</template>

<script setup>
import { reactive } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue'
});
</script>

reactive 对象转换为 ref 对象

当你需要将 reactive 对象的属性作为 ref 使用时,可以使用 toRefs 函数。这在你需要将 reactive 对象的属性单独作为 ref 传递给其他组件时非常有用。

javascript 复制代码
import { reactive, toRefs } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue'
});

// 将 reactive 对象转换为 ref 对象
const stateRefs = toRefs(state);

// 现在 stateRefs.count 是一个 ref 对象
console.log(stateRefs.count.value); // 输出 0

注意事项

  1. 不可变数据结构 :Vue 3 不支持直接对数组或内置对象(如 MapSet)使用 refreactive 来创建响应式数据。对于这些类型,你需要使用 reactive 包装整个对象,然后操作对象内的数组或集合。

  2. 访问和修改 :访问 ref 创建的响应式数据时,需要通过 .value 属性。修改时也直接操作 .value

  3. 模板语法糖 :在模板中,Vue 提供了语法糖,允许你直接使用 ref 而不需要 .value,Vue 会自动解包。

通过 refreactive,Vue 3 的组合式 API 提供了灵活的方式来创建和管理响应式状态,使得状态管理更加直观和高效。

六、响应式基础

本段内容来自官方文档。

声明响应式状态

ref()

在组合式 API 中,推荐使用 [ref()] 函数来声明响应式状态:

js

js 复制代码
import { ref } from 'vue'

const count = ref(0)

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

js

js 复制代码
const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

要在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们:

js

js 复制代码
import { ref } from 'vue'

export default {
  // `setup` 是一个特殊的钩子,专门用于组合式 API。
  setup() {
    const count = ref(0)

    // 将 ref 暴露给模板
    return {
      count
    }
  }
}

template

vue 复制代码
<div>{{ count }}</div>

注意,在模板中使用 ref 时,我们 需要附加 .value。为了方便起见,当在模板中使用时,ref 会自动解包。

你也可以直接在事件监听器中改变一个 ref:

template

vue 复制代码
<button @click="count++">
  {{ count }}
</button>

对于更复杂的逻辑,我们可以在同一作用域内声明更改 ref 的函数,并将它们作为方法与状态一起公开:

js

js 复制代码
import { ref } from 'vue'

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

    function increment() {
      // 在 JavaScript 中需要 .value
      count.value++
    }

    // 不要忘记同时暴露 increment 函数
    return {
      count,
      increment
    }
  }
}

然后,暴露的方法可以被用作事件监听器:

template

vue 复制代码
<button @click="increment">
  {{ count }}
</button>

<script setup>

setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用[单文件组件 (SFC)来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:

vue

js 复制代码
<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }}
  </button>
</template>
### 设置 getter 和 setter

`computed` 也可以接受一个包含 `get` 和 `set` 函数的对象,这允许你创建一个可写的计算属性。

```javascript
import { computed, ref } from 'vue';

const count = ref(0);

const doubledCount = computed({
  // getter
  get: () => count.value * 2,
  // setter
  set: (newValue) => {
    count.value = newValue / 2;
  }
});

在这个例子中,doubledCount 既可以读取也可以设置。当设置 doubledCount 的值时,count 的值会相应地更新。

计算属性的缓存机制

计算属性是缓存的,只有当计算属性的响应式依赖发生变化时,计算属性才会重新计算。这意味着只要依赖项没有变化,多次访问计算属性会立即返回之前的计算结果,而不会再次执行计算函数。

计算属性与方法的区别

虽然你可以使用方法来执行类似的计算,但计算属性提供了缓存机制,这使得计算属性在依赖项未改变时更加高效。此外,计算属性的依赖项是自动跟踪的,而方法则需要手动处理依赖关系。

总结

computed 是 Vue 3 组合式 API 中一个强大的工具,它允许你创建依赖于响应式数据的缓存计算属性。这使得你可以构建复杂的逻辑,同时保持应用的性能和响应性。通过使用 computed,你可以确保只有在必要时才重新计算值,从而优化你的应用性能。

八、侦听器

在 Vue 3 的组合式 API 中,watchwatchEffect 是两个用于侦听响应式数据变化的函数。它们允许你观察和响应 Vue 应用中的状态变化,这对于执行副作用(如数据获取、状态更新等)非常有用。

watchEffect 函数

watchEffect 函数会立即执行传入的函数,并在其依赖的响应式数据发生变化时重新执行。

基本用法

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

const state = ref(0);

watchEffect(() => {
  console.log(state.value); // 每当 state 发生变化时,这里的代码都会执行
});

在这个例子中,任何对 state 的修改都会触发 console.log 的执行。

参数

watchEffect 可以接受一个函数作为参数,这个函数会在 watchEffect 被创建时立即执行一次,并且每当其依赖的响应式数据发生变化时再次执行。

返回值

watchEffect 返回一个停止侦听的函数,你可以调用这个函数来停止侦听。

javascript 复制代码
const stop = watchEffect(() => {
  console.log(state.value);
});

// 稍后停止侦听
stop();

watch 函数

watch 函数用于侦听一个或多个响应式数据源,并在其变化时执行副作用。

基本用法

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

const state = ref(0);

watch(state, (newValue, oldValue) => {
  console.log(`state从${oldValue}变化到${newValue}`); // 当 state 发生变化时,这里的代码都会执行
});

在这个例子中,watch 侦听了 state 的变化,并在变化时打印新旧值。

参数

watch 接受两个参数:第一个是要侦听的响应式数据源,第二个是一个回调函数,该函数会在侦听的数据源变化时被调用。

选项

watch 还接受一个可选的选项对象,其中可以配置立即执行(immediate)、侦听深度(deep)等。

javascript 复制代码
watch(
  () => state.value,
  {
    immediate: true, // 立即执行回调函数
    deep: true // 深度侦听,对于对象和数组有效
  },
  (newValue, oldValue) => {
    console.log(`state从${oldValue}变化到${newValue}`);
  }
);

区别和选择

  • watchEffect:自动侦听其依赖的响应式数据,并且没有新旧值的概念,它只是依赖发生变化时重新执行。
  • watch:允许你指定具体的响应式数据源,并在数据变化时提供新旧值,更灵活。

使用场景

  • watchEffect:当你需要执行副作用,并且这个副作用依赖于多个响应式数据源时。
  • watch:当你需要在响应式数据变化时执行具体的逻辑,并且需要知道变化前后的值时。

通过使用 watchwatchEffect,你可以在 Vue 3 的组合式 API 中有效地管理和响应状态变化,使得你的应用能够根据状态的变化执行相应的操作。

九、生命周期钩子

在 Vue 3 的组合式 API 中,生命周期钩子函数允许你在组件的不同阶段执行代码。这些生命周期钩子函数与选项式 API 中的生命周期钩子相对应,但它们专为 setup 函数设计,使得在组合式 API 中管理组件的生命周期变得更加直接和灵活。

以下是 Vue 3 组合式 API 提供的一些主要生命周期钩子函数:

onMounted

onMounted 钩子函数在组件的 DOM 挂载完成后被调用。这与选项式 API 中的 mounted 钩子相对应。

基本用法

javascript 复制代码
import { onMounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      console.log('组件已挂载到 DOM');
      // 执行 DOM 操作或执行副作用
    });
  }
};

onUpdated

onUpdated 钩子函数在组件更新后被调用,这包括响应式数据的变化导致的重新渲染。这与选项式 API 中的 updated 钩子相对应。

基本用法

javascript 复制代码
import { onUpdated } from 'vue';

export default {
  setup() {
    onUpdated(() => {
      console.log('组件已更新');
      // 响应组件更新
    });
  }
};

onUnmounted

onUnmounted 钩子函数在组件卸载和销毁之前被调用。这与选项式 API 中的 beforeDestroydestroyed 钩子相对应。

基本用法

javascript 复制代码
import { onUnmounted } from 'vue';

export default {
  setup() {
    onUnmounted(() => {
      console.log('组件即将卸载');
      // 清理工作,如取消订阅、定时器等
    });
  }
};

onBeforeMount

onBeforeMount 钩子函数在组件挂载之前被调用,此时组件的模板已经渲染为 HTML,但还没有被添加到 DOM 中。

基本用法

javascript 复制代码
import { onBeforeMount } from 'vue';

export default {
  setup() {
    onBeforeMount(() => {
      console.log('组件即将挂载');
      // 执行挂载前的准备工作
    });
  }
};

onBeforeUpdate

onBeforeUpdate 钩子函数在组件更新之前被调用。

基本用法

javascript 复制代码
import { onBeforeUpdate } from 'vue';

export default {
  setup() {
    onBeforeUpdate(() => {
      console.log('组件即将更新');
      // 执行更新前的准备工作
    });
  }
};

onBeforeUnmount

onBeforeUnmount 钩子函数在组件卸载之前被调用。

基本用法

javascript 复制代码
import { onBeforeUnmount } from 'vue';

export default {
  setup() {
    onBeforeUnmount(() => {
      console.log('组件即将卸载');
      // 执行卸载前的准备工作
    });
  }
};

onActivatedonDeactivated

这两个钩子函数用于保持活动状态的组件(如 <keep-alive> 缓存的组件)。

基本用法

javascript 复制代码
import { onActivated, onDeactivated } from 'vue';

export default {
  setup() {
    onActivated(() => {
      console.log('组件被激活');
    });

    onDeactivated(() => {
      console.log('组件被停用');
    });
  }
};

注意事项

  • 生命周期钩子函数应该在 setup 函数中调用,因为它们依赖于组合式 API 的响应式系统。
  • 生命周期钩子函数提供了一种在组件的不同阶段执行代码的机制,使得组件的控制更加灵活和精确。
  • 使用生命周期钩子函数时,确保不要在函数中执行异步操作而忘记清理,这可能会导致内存泄漏。

通过这些生命周期钩子函数,你可以更好地控制组件的生命周期,执行必要的初始化和清理工作,以及响应组件状态的变化。

十、依赖注入

在 Vue 3 中,provideinject 是一对函数,它们允许跨组件树进行状态共享,而不必通过每个层级的组件逐级传递 props。这种依赖注入的模式非常适合于需要在多个不直接相关的组件之间共享状态的场景。

provide

provide 函数用于向所有子孙组件提供数据。它应该在组件的 setup 函数中调用,并且可以提供任何可响应式的数据或方法。

基本用法

javascript 复制代码
import { provide, reactive } from 'vue';

export default {
  setup() {
    const state = reactive({
      theme: 'dark',
      toggleTheme: () => {
        state.theme = state.theme === 'dark' ? 'light' : 'dark';
      }
    });

    provide('themeState', state); // 提供响应式状态

    return {
      state
    };
  }
};

在这个例子中,state 对象被提供给所有子孙组件,它们可以通过 inject 函数访问 themetoggleTheme 方法。

inject

inject 函数用于接收从祖先组件提供的响应式数据。它应该在组件的 setup 函数中调用。

基本用法

javascript 复制代码
import { inject } from 'vue';

export default {
  setup() {
    const themeState = inject('themeState'); // 接收提供的状态

    if (!themeState) {
      console.error('themeState is not provided');
    }

    return {
      themeState
    };
  }
};

在这个例子中,组件接收了祖先组件提供的 themeState 对象,并将其作为响应式数据使用。

provideinject 的选项

provideinject 都可以接受第二个参数,这是一个选项对象,允许你定义默认值和是否允许注入为 undefined

javascript 复制代码
// 提供默认值
provide('themeState', state, { default: () => ({}) });

// 允许注入为 undefined
provide('themeState', state, { from: 'parent', default: null });

注意事项

  1. 响应式provideinject 都支持响应式数据,但 inject 接收的是非响应式的副本,如果需要响应式引用,可以使用 inject 的响应式版本 injectReactive

  2. 祖先组件provide 必须在其子孙组件的祖先组件中调用,否则 inject 将无法接收到提供的值。

  3. 跨组件通信provideinject 是跨组件通信的一种方式,适用于需要在多个层级间共享状态的场景。

  4. 命名冲突:避免使用相同的键在不同的祖先组件中提供不同的值,这可能会导致命名冲突。

  5. 清理 :由于 provideinject 建立的是响应式连接,所以在组件卸载时,Vue 会自动处理这些连接的清理工作。

通过使用 provideinject,你可以在 Vue 3 的组合式 API 中实现跨组件的状态共享,使得状态管理更加集中和高效。

十一、应用示例

创建一个 Vue 3 项目实例,这个项目将是一个简单的待办事项(Todo)应用。我们将使用 refreactivecomputedwatch 和生命周期钩子来构建这个应用。

1. 初始化项目

使用 Vite 创建一个新的 Vue 3 项目:

bash 复制代码
npm create vite@latest my-todo-app -- --template vue
cd my-todo-app
npm install

2. 创建 Todo 组件

src 目录下创建一个新的组件文件 TodoList.vue

vue 复制代码
<template>
  <div>
    <h1>Todo List</h1>
    <input v-model="newTodo" @keyup.enter="addTodo" placeholder="Add a new todo">
    <ul>
      <li v-for="todo in filteredTodos" :key="todo.id">
        <input type="checkbox" v-model="todo.completed">
        <span :class="{ 'completed': todo.completed }">{{ todo.task }}</span>
        <button @click="removeTodo(todo.id)">Remove</button>
      </li>
    </ul>
    <button @click="toggleFilter">Show {{ filter === 'all' ? 'All' : 'Active' }} Todos</button>
    <p>Active Todos: {{ activeTodos.length }}</p>
  </div>
</template>

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

// 使用 reactive 创建响应式状态对象
const state = reactive({
  todos: [],
  newTodo: '',
  filter: 'all',
});

// 使用 ref 创建单个响应式引用
const nextTodoId = ref(0);

// 计算属性,过滤待办事项
const filteredTodos = computed(() => {
  if (state.filter === 'all') {
    return state.todos;
  } else {
    return state.todos.filter(todo => !todo.completed);
  }
});

// 计算属性,计算活跃待办事项的数量
const activeTodos = computed(() => {
  return state.todos.filter(todo => !todo.completed);
});

// 监听新待办事项输入框的变化
watch(() => state.newTodo, (newVal) => {
  if (newVal === '') {
    // 如果输入框为空,则重置下一个待办事项的 ID
    nextTodoId.value = 0;
  }
});

// 生命周期钩子,组件挂载后执行
onMounted(() => {
  console.log('TodoList component is mounted');
});

// 方法,添加新的待办事项
function addTodo() {
  if (state.newTodo.trim()) {
    state.todos.push({
      id: nextTodoId.value++,
      task: state.newTodo,
      completed: false,
    });
    state.newTodo = '';
  }
}

// 方法,移除待办事项
function removeTodo(id) {
  state.todos = state.todos.filter(todo => todo.id !== id);
}

// 方法,切换过滤条件
function toggleFilter() {
  state.filter = state.filter === 'all' ? 'active' : 'all';
}
</script>

<style scoped>
.completed {
  text-decoration: line-through;
}
</style>

3. 在 App.vue 中使用 Todo 组件

编辑 src/App.vue 文件,引入并使用 TodoList 组件:

vue 复制代码
<template>
  <div id="app">
    <TodoList />
  </div>
</template>

<script>
import TodoList from './components/TodoList.vue';

export default {
  name: 'App',
  components: {
    TodoList
  }
};
</script>

<style>
/* 全局样式 */
</style>

4. 运行项目

使用以下命令启动开发服务器:

bash 复制代码
npm run dev

现在,你可以在浏览器中访问 http://localhost:3000 来查看你的 Vue 3 Todo 应用。

这个项目实例展示了如何使用 Vue 3 的组合式 API 来创建一个待办事项应用。它包括了 refreactivecomputedwatch 和生命周期钩子的使用。这个应用允许用户添加、删除待办事项,并可以过滤显示所有或活跃的待办事项。你可以根据需要扩展这个项目,添加更多的功能,比如编辑待办事项、保存待办事项到本地存储等。

相关推荐
Mirilla4 分钟前
华为iotda sdk发送消息无法更新quickstartpython问题解决
java·服务器·前端
zooKevin6 分钟前
nodeJS下npm和yarn的关系和区别详解
javascript
秋风~微凉10 分钟前
【问题记录】npm create vue@latest报错
前端·vue.js·npm
fuze233319 分钟前
ElementUI中的el-select显示的不是对应label值
前端·javascript·elementui
LCG元21 分钟前
Vue.js组件开发-在setup函数中使用provide
vue.js
前端小凯23 分钟前
vue3使用setup语法糖组件基础传值
前端·javascript·vue.js
customer0823 分钟前
【开源免费】基于SpringBoot+Vue.JS作业管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
后端转全栈_小伵24 分钟前
Vue 项目自动化部署:Coding + Jenkins + Nginx 实践分享
前端·nginx·vue·jenkins·自动化部署·coding
黑客Jack32 分钟前
docker、数据库、Web应用程序安全
前端·数据库·docker
阿雄不会写代码37 分钟前
如何使用axios实现并发请求
前端·javascript·vue.js