详细分析Vue3中组合式API(附Demo)

前言

对于Vue2转Vue3的项目中,此文了解所必备的特性知识点

1. 基本知识

Vue 3 引入了组合式 API(Composition API),这是一个强大的新功能,与传统的选项式 API(Options API)相比,提供了更灵活、更易于复用的方式来组织和复用代码

  1. setup 函数
    组合式 API 的核心
    每个组件的 setup 函数在组件实例创建之前执行,用于定义组件的响应式状态和逻辑
    setup 函数接收两个参数:propscontext
  1. 响应式数据
  1. 计算属性

computed:创建一个计算属性,依赖的值变化时自动更新

  1. 侦听器
  • watch:侦听响应式数据的变化并执行副作用
  • watchEffect:立即执行传入的副作用函数,并在其依赖项发生变化时重新执行
  1. 生命周期钩子

在组合式 API 中,生命周期钩子通过 onXxx 函数来实现,例如 onMounted, onUpdated, onUnmounted

2. Demo

感受两者的差异

特性/方面选项式 API组合式 API
定义组件状态使用 data 选项,返回一个包含状态的对象使用 reactive 或 ref 函数
定义方法使用 methods 选项定义方法在 setup 函数中定义方法
计算属性使用 computed 选项定义使用 computed 函数
侦听器使用 watch 选项定义使用 watch 和 watchEffect 函数
生命周期钩子使用 created, mounted 等选项使用 onMounted, onUpdated 等函数
代码组织逻辑分散在不同选项中,如 data, methods 等逻辑集中在 setup 函数中,便于组织和复用
复用逻辑通过混入 (mixins) 或高阶组件 (HOC) 复用逻辑通过组合函数 (composition functions) 复用逻辑
类型支持对 TypeScript 支持有限,类型推断不够强对 TypeScript 支持更好,类型推断更强
模块化逻辑复用和模块化较难,代码容易变得复杂更易模块化,逻辑复用更灵活
性能大多数场景下性能相当,但逻辑复用场景下性能较低更高的性能和更低的内存占用,尤其在复杂逻辑中

2.1 选项式

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script>
export default {
  // data 选项用于定义组件的响应式状态
  data() {
    return {
      count: 0
    };
  },
  // methods 选项用于定义组件的方法
  methods: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    }
  }
};
</script>

2.2 组合式

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

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

export default {
  setup() {
    // 创建一个响应式的 count 变量
    const count = ref(0);

    // 定义递增函数
    const increment = () => {
      count.value++;
    };

    // 定义递减函数
    const decrement = () => {
      count.value--;
    };

    // 返回供模板使用的数据和方法
    return {
      count,
      increment,
      decrement
    };
  }
};
</script>

3. 细节

3.1 setup

在组件实例创建之前执行,用于初始化组件的状态和定义组件的逻辑

setup(props, context) {
  const { attrs, slots, emit } = context;
}

setup 函数返回一个对象,该对象的属性和方法会被暴露给组件的模板

  • 响应式数据(由 ref 或 reactive 创建)
  • 计算属性(由 computed 创建)
  • 方法(普通函数)
setup() {
  const count = ref(0);
  const increment = () => {
    count.value++;
  };
  return { count, increment };
}

需要注意的点:

  • 在 setup 中,不能使用 this 访问组件实例,因为组件实例在 setup 执行时尚未创建
  • 组合式 API 中的生命周期钩子函数(如 onMounted, onUnmounted)需要在 setup 函数中调用,而不是在组件选项中定义
  • props 是响应式的,可以解构使用,但不建议解构,因为这会导致响应性丢失。使用时应直接使用 props 对象
  • context 的 attrs, slots, 和 emit 也是响应式的,可以用于更灵活的组件开发

示例Demo如下:

<template>
  <div v-bind="attrs" @click="handleClick">
    <p>{{ message }}</p>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <slot></slot>
  </div>
</template>

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

export default {
  props: {
    initialCount: {
      type: Number,
      default: 0
    }
  },
  setup(props, { attrs, slots, emit }) {
    // 响应式状态
    const count = ref(props.initialCount);
    const state = reactive({ message: 'Hello Vue 3' });

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

    // 侦听器
    watch(count, (newVal, oldVal) => {
      console.log(`Count changed from ${oldVal} to ${newVal}`);
    });

    // 方法
    const handleClick = () => {
      emit('custom-event', count.value);
    };

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

    return {
      count,
      state,
      doubleCount,
      handleClick,
      attrs,  // 可在模板中使用
      slots   // 可在模板中使用
    };
  }
};
</script>

3.2 watch

Vue 3 中,watch 用于监听响应式数据的变化并在变化时执行特定的副作用

watch 在组合式 API 中提供了更灵活的监听机制,相较于 Vue 2 的 watch 选项,Vue 3 的 watch 有一些重要的变化和增强

特性/方面Vue 2 选项式 APIVue 3 组合式 API
定义位置在组件选项的 watch 对象中在 setup 函数中使用 watch 函数
监听多个源不支持支持
即时执行回调不支持支持,通过 { immediate: true } 选项
深度监听通过 { deep: true } 选项支持,通过 { deep: true } 选项
回调参数新值和旧值新值和旧值
可组合性逻辑分散在不同的选项中逻辑集中在 setup 函数中,更易组织和复用
  • 可以监听 ref、reactive 对象以及计算属性的变化
  • 监听多个响应式数据源,并在任一源变化时触发回调
  • 通过选项配置立即执行回调函数,而不仅是在数据变化时
  • 深度监听嵌套对象的变化
  • 回调函数接收两个参数:新值和旧值,可以更灵活地处理变化

基本的用法如下:

import { ref, watch } from 'vue';

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

    watch(count, (newValue, oldValue) => {
      console.log(`Count changed from ${oldValue} to ${newValue}`);
    });

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

    return {
      count,
      increment
    };
  }
};
  • 监听多个源:
import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const name = ref('Vue');

    watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
      console.log(`Count changed from ${oldCount} to ${newCount}`);
      console.log(`Name changed from ${oldName} to ${newName}`);
    });

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

    const changeName = () => {
      name.value = name.value === 'Vue' ? 'Vue.js' : 'Vue';
    };

    return {
      count,
      name,
      increment,
      changeName
    };
  }
};
  • 深度监听嵌套对象
import { reactive, watch } from 'vue';

export default {
  setup() {
    const user = reactive({
      name: 'John Doe',
      address: {
        city: 'New York',
        zip: '10001'
      }
    });

    watch(user, (newValue, oldValue) => {
      console.log('User changed:', newValue);
    }, { deep: true });

    const changeAddress = () => {
      user.address.city = 'Los Angeles';
    };

    return {
      user,
      changeAddress
    };
  }
};
  • 立即执行回调
import { ref, watch } from 'vue';

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

    watch(count, (newValue, oldValue) => {
      console.log(`Count changed from ${oldValue} to ${newValue}`);
    }, { immediate: true });

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

    return {
      count,
      increment
    };
  }
};

3.3 computed

在 Vue 3 中,computed 是一个独立的 API,可以在组合式 API 中使用
在 Vue 2 中,计算属性是在组件选项中定义的

Vue2的用法:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  computed: {
    doubleCount() {
      return this.count * 2;
    }
  }
};
</script>

Vue3:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
  </div>
</template>

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

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

    return {
      count,
      doubleCount
    };
  }
};
</script>

带有 Getter 和 Setter 的计算属性
Vue2:

<template>
  <div>
    <p>Full Name: {{ fullName }}</p>
    <input v-model="firstName" placeholder="First Name">
    <input v-model="lastName" placeholder="Last Name">
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    fullName: {
      get() {
        return `${this.firstName} ${this.lastName}`;
      },
      set(newValue) {
        const names = newValue.split(' ');
        this.firstName = names[0];
        this.lastName = names[1] || '';
      }
    }
  }
};
</script>

Vue3:

<template>
  <div>
    <p>Full Name: {{ fullName }}</p>
    <input v-model="firstName" placeholder="First Name">
    <input v-model="lastName" placeholder="Last Name">
  </div>
</template>

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

export default {
  setup() {
    const firstName = ref('John');
    const lastName = ref('Doe');

    const fullName = computed({
      get() {
        return `${firstName.value} ${lastName.value}`;
      },
      set(newValue) {
        const names = newValue.split(' ');
        firstName.value = names[0];
        lastName.value = names[1] || '';
      }
    });

    return {
      firstName,
      lastName,
      fullName
    };
  }
};
</script>
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农研究僧

你的鼓励将是我创作的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值