Vue 3教程:Composition API与组件开发

Vue 3 教程:Composition API 与组件开发

Vue.js 3 作为当下最流行的前端框架之一,带来了许多令人兴奋的新特性,其中 Composition API 尤为引人注目。它不仅提供了一种全新的、更灵活的组织组件逻辑的方式,还增强了代码的可重用性、可读性和可维护性。本文将深入探讨 Composition API 的核心概念、用法,并结合组件开发的实践,帮助你全面掌握这一强大的工具。

一、Composition API 简介:为什么需要它?

在 Vue 2 中,我们主要使用 Options API 来编写组件。Options API 将组件的选项(如 datamethodscomputedwatch 等)组织在一个对象中。这种方式在小型组件中清晰明了,但随着组件变得复杂,代码会散布在不同的选项中,导致以下问题:

  • 逻辑关注点分散: 与同一逻辑关注点相关的代码(例如,处理用户输入、获取数据、更新状态)分散在不同的选项中,难以追踪和理解。
  • 代码复用困难: Options API 主要通过 mixins 来实现代码复用,但 mixins 容易导致命名冲突、数据来源不清晰等问题。
  • 类型推断限制: Options API 对 TypeScript 的支持不够友好,类型推断能力有限。

Composition API 的出现正是为了解决这些问题。它提供了一种基于函数的 API,让我们可以将与同一逻辑关注点相关的代码组织在一起,形成一个个独立的、可复用的“组合式函数”(Composable Function)。

二、Composition API 核心概念

Composition API 的核心在于 setup 函数和一系列响应式 API。

1. setup 函数:组件逻辑的入口

setup 函数是 Composition API 的入口点。它在组件创建之前执行,接收两个参数:

  • props 一个响应式的对象,包含父组件传递给当前组件的数据。
  • context 一个普通的 JavaScript 对象,提供了一些有用的属性,如 attrsslotsemit 等。

setup 函数的返回值是一个对象,该对象中的属性将被暴露给组件的模板使用。

```vue

```

关键点:

  • setup 函数在组件实例创建之前执行,因此你无法在 setup 函数中访问 this
  • props 是响应式的,但不要解构 props,否则会失去响应性。如果需要解构,可以使用 toRefs
  • context 提供了对组件实例的一些属性的访问,但是 context 不是响应式的。

2. 响应式 API:构建响应式数据

Composition API 提供了一系列函数来创建和操作响应式数据:

  • ref 用于创建一个响应式引用。它接受一个初始值,并返回一个包含 .value 属性的对象。对 .value 的修改会触发视图更新。

    javascript
    const count = ref(0);
    console.log(count.value); // 0
    count.value++;
    console.log(count.value); // 1

    * reactive 用于创建一个响应式对象。它接受一个普通对象,并返回一个响应式的代理对象。对代理对象属性的修改会触发视图更新。

    javascript
    const state = reactive({
    count: 0,
    message: 'Hello',
    });
    console.log(state.count); // 0
    state.count++;
    console.log(state.count); // 1

    * computed 用于创建一个计算属性。它接受一个 getter 函数,并返回一个只读的响应式引用。计算属性的值会根据其依赖的响应式数据自动更新。
    javascript
    import {ref, computed} from 'vue'
    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    console.log(plusOne.value) // 2

    也可以传入一个带有get和set函数的对象
    javascript
    import {ref, computed} from 'vue'
    const count = ref(1)
    const plusOne = computed({
    get: () => count.value + 1,
    set: (val) => {
    count.value = val - 1
    }
    })

    * watchEffect 用于创建一个副作用。它接受一个函数,该函数会在其依赖的响应式数据发生变化时立即执行。

    javascript
    watchEffect(() => {
    console.log('Count:', count.value);
    });

    * watch 用于监视一个或多个响应式数据源。它接受一个或多个数据源、一个回调函数和一个可选的配置对象。当数据源发生变化时,回调函数会被执行。

    ```javascript
    watch(count, (newValue, oldValue) => {
    console.log('Count changed:', newValue, oldValue);
    });

    // 监视多个数据源
    watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
    console.log('Data changed:', newCount, newMessage, oldCount, oldMessage);
    });
    ```

    watch 还可以直接监视一个 getter 函数:
    javascript
    watch(
    () => state.count,
    (count, prevCount) => {
    /* ... */
    }
    )

    * toRefs: 将一个响应式对象转换为一个普通对象,其中每个 property 都是一个 ref ,指向原始对象中的相应 property。这在你想要将响应式对象的属性传递给组合式函数,但又希望它们保持响应性时非常有用。

```javascript
const state = reactive({
foo: 1,
bar: 2
})

const stateAsRefs = toRefs(state)
/
stateAsRefs 的类型:
{
foo: Ref,
bar: Ref
}
/
// ref 和 state.foo 的值是同步的
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
```

  • readonly: 接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
    ```javascript
    const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
// 依赖追踪
console.log(copy.count)
})

// original 的修改会触发 copy 上的 effect
original.count++

// 无法修改 copy
copy.count++ // warning!
```

三、组合式函数:逻辑复用

组合式函数是 Composition API 的核心优势之一。它们是普通的 JavaScript 函数,利用 Composition API 来封装和复用组件逻辑。

示例:一个简单的计数器组合式函数

```javascript
// useCounter.js
import { ref } from 'vue';

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

function increment() {
count.value++;
}

function decrement() {
count.value--;
}

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

在组件中使用组合式函数

```vue

```

组合式函数的优势:

  • 逻辑清晰: 将相关的逻辑封装在一个函数中,易于理解和维护。
  • 代码复用: 可以在多个组件中复用同一个组合式函数,避免代码重复。
  • 可测试性: 组合式函数是纯 JavaScript 函数,易于进行单元测试。
  • 更好的TypeScript集成 因为组合式函数主要利用基本的变量和函数,它们天生就是类型友好的。

四、组件开发实践

1. 生命周期钩子

在 Composition API 中,你可以使用 onX 系列函数来注册生命周期钩子。这些函数需要在 setup 函数中同步调用。

```javascript
import { onMounted, onUpdated, onUnmounted } from 'vue';

export default {
setup() {
onMounted(() => {
console.log('Component mounted!');
});

onUpdated(() => {
  console.log('Component updated!');
});

onUnmounted(() => {
  console.log('Component unmounted!');
});

},
};
```

常用的生命周期钩子和vue2中生命周期钩子的对应关系:

Vue2 Vue3 描述
beforeCreate (在 setup 中直接执行) 组件实例初始化之前执行
created (在 setup 中直接执行) 组件实例初始化之后执行
beforeMount onBeforeMount 组件挂载到 DOM 之前执行
mounted onMounted 组件挂载到 DOM 之后执行
beforeUpdate onBeforeUpdate 组件更新之前执行
updated onUpdated 组件更新之后执行
beforeUnmount onBeforeUnmount (Vue 3.2+) 组件卸载之前执行
beforeDestroy onBeforeUnmount 组件卸载之前执行 (Vue 3.2 之前)
unmounted onUnmounted (Vue 3.2+) 组件卸载之后执行
destroyed onUnmounted 组件卸载之后执行 (Vue 3.2 之前)
errorCaptured onErrorCaptured 捕获子组件错误时执行
activated onActivated <keep-alive> 组件激活时执行
deactivated onDeactivated <keep-alive> 组件停用时执行

2. 模板引用 (Template Refs)

在 Composition API 中,你可以使用 ref 来创建模板引用。

```vue

对于在 v-for 循环中使用的 ref, ref 的值将会是一个包含了对应整个列表的 ref 的数组:vue

```

3. Provide / Inject

provideinject 提供依赖注入的用法。允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
provideinject 通常会在不同的组件中运行。为了在组合式 API 中使用它们,我们需要使用到 provide 和 inject 函数。
```vue
// 在父组件中提供数据
import { provide, ref } from 'vue';
const count = ref(0);
provide('count', count);
//后代组件中使用
import { inject } from 'vue';
const count = inject('count');

**注意:** `provide` 和 `inject` 绑定的**不是**响应式的。然而,如果你传入了一个响应式对象,那么其上的 property 还是会保持响应式的。
但是我们可以手动传入一个ref来使其变为响应式。
vue
// 祖先组件
provide('count', count)
//后代组件
const count = inject('count') // count 是一个 ref
为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 `computed`:vue
// 祖先组件
provide('readOnlyCount', computed(() => count.value))
**应用层 Provide**
除了在一个组件中提供数据,我们还可以在整个应用层面提供数据:
javascript
//main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.provide(/ 注入名 / 'message', // 'hello!')
app.mount('#app')
```
在应用内的所有组件中,都可以注入 'message' 这个值。这在你为插件做开发时会很有用,因为插件通常不会使用一个组件来注册全局可用的值。

五、Composition API 与 Options API 的对比

特性 Options API Composition API
组织方式 基于选项(datamethodscomputed 等) 基于函数
逻辑关注点 分散在不同的选项中 集中在同一个函数中
代码复用 Mixins 组合式函数
类型推断 支持较弱 支持更好
代码可读性 小型组件清晰,大型组件复杂 无论组件大小,都更易于理解和维护
学习曲线 相对平缓 需要理解响应式系统和函数式编程的概念

六、总结

Composition API 是 Vue 3 中一项重要的革新。它提供了一种更灵活、更强大、更易于维护的方式来编写组件。通过掌握 Composition API,你可以:

  • 编写更清晰、更易于理解的代码。
  • 更好地组织和复用组件逻辑。
  • 充分利用 TypeScript 的类型推断能力。
  • 构建更复杂、更强大的 Vue 应用程序。

希望本文能够帮助你深入理解 Vue 3 Composition API,并在实际开发中熟练运用它。虽然 Composition API 具有陡峭的学习曲线,但我相信一旦掌握了这些概念和技术,你将会发现它在构建复杂且可维护的应用程序方面所带来的巨大价值。

THE END