vue3的Composition API

2024-08-04 10:19:52 浏览数 (2)

什么是 Composition API?

Composition API 也叫组合式API, 是在vue3中新引入的一种API,vue2中已经有option API了,那为什么要新稿这么一套呢,其实主要原因是要解决vue2中的option API的在处理复杂组件逻辑的局限性,例如逻辑分散、代码复用性差、类型推断困难、组件组织混乱、响应式系统限制、模板逻辑复杂性、组件测试困难等问题。为此Composition API通过函数的方式来组织代码,使得逻辑更加模块化和可组合,这就变得很灵活。

核心概念

setup 函数

setup 是使用组合式API的入口函数,用于替代vue2中的data、computed 、methods等选项,setup函数参数为(props, context)props可以理解为vue2中的props属性,用来访问父级传来的参数值。

context 则是一个对象集合包括:

  • attrs:包含了父组件传递给子组件的所有属性(非prop的属性),它们是响应式的,可以动态地绑定到模板中。
  • slots:包含了所有传入的插槽内容,这些内容可以用于渲染作用域插槽。
  • emit:是一个函数,用于向父组件发出自定义事件。它是this.$emit的替代。
  • expose:是一个函数,用于显式地暴露子组件内部的属性或方法,使得父组件可以通过ref访问到这些属性或方法。

setup声明方式有两种语法方式

第一种

代码语言:javascript复制
<script>
export default {
  props: {
    msg: String
  },
  setup(props) {
    console.log('setup',props.msg)
  }
}
</script>

第二种 更简洁 官方称为更符合人体工程学

但 props 要更改写法,使用defineProps,放心虽然没有声明,但它将自动在setup 中可用

代码语言:javascript复制
<script setup>
defineProps({
  msg: String,
})
</script>

核心api

ref

ref 用于创建一个可修改的响应式的基本数据类型或引用(可以对 .value 赋值来进行修改, 如果将一个对象赋值给 ref,那这个对象则通过 reactive 转化为深层次响应式的数据),

如果这个对象中存在ref也会被深层解包,为避免这种深层次的转化,官方建议用 shallowRef替代

使用方法

代码语言:javascript复制
// .vue
<template>
  <h1>{{age}}</h1>
</template>
<script setup>
import { ref } from 'vue'

let age = ref(18)

age.value = 20
</script>

reactive

reactive 用于创建一个响应式对象。有一点需要注意,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

只有当你直接访问这个ref内部的值时,比如使用.value属性,Vue才会“解包”这个ref,把它内部的值当作响应式值进行追踪。

这样做的好处是提高了性能,避免了不必要的响应式转换,因为在某些情况下,你可能并不需要数组或Map中的每个ref元素都是响应式的。

所以要避免深层响应式的转换,只想保留对这个对象顶层次访问的响应式,可以用shallowReactive做为替代

代码语言:javascript复制
import { ref, reactive } from 'vue'
let age = ref(18)
let obj = reactive(['123',age])

// 直接访问数组中的 obj 元素,不会执行解包
console.log(obj[1]);  // 输出是 ref 对象本身,而不是 18

// 要获取 obj 中的值,需要使用.value
console.log(obj[1].value);  // 输出 18

</script>

readonly

readonly和 reacitve 类似,只不过从字面意义上就不难理解,only 它是只读的,而且代理是深层的,所以同理要避免深层层级的转换行为,可以用shallowReadonly 来替代

代码语言:javascript复制
...
<!--19-->
<div>{{ man.age }}</div> 
<!--19-->
<div>{{ man2.age }}</div>
<!--20-->
<div>{{ man3.age }}</div>
...
import { reactive, ref, readonly, watchEffect } from 'vue'
...
let man = reactive({ age: 18 })

let man2 = readonly(man)

let man3 = readonly({age: 20})
watchEffect(() => {
  // 用来做响应性追踪
  console.log(man.age)
})

// 会触发 watchEffect
man.age  

// 下面man2和man3的操作会执行失败而且会触发警告
// Set operation on key "age" failed: target is readonly.
man2.age  

man3.age  

computed、watch

computed、watch和vue2中的含义相同

computed用法

代码语言:javascript复制
...
<h1 @click="count   ">{{ addCount }}</h1>
...
import { ref, reactive } from 'vue'
...


let count = ref(1)
const addCount = computed(() => count.value   1)
// addCount.value    // 错误写法,因为addCount是只读的 会报错

watch 默认懒监听: 仅在监听源发生变化时才执行回调函数

watch一共三个参数,watch(source,callback,options)

  • source: 这个参数是要侦听的响应式引用或响应式对象的属性,或者是返回响应式值的getter函数。 可以是 ref、reactive 对象、computed 计算属性或者一个自定义的getter函数。
  • callback:当侦听的源发生变化时会被调用的回调函数。这个函数接收三个参数:新值、旧值和onCleanup函数。onCleanup可以用来注册清理回调,在下次侦听器执行前会被调用。
  • options (可选): 包含配置选项json对象
    • immediate: 值为true,会在侦听器创建时立即执行回调。
    • deep: 值为true 会深度监听对象内部的变化。
    • flush: 指定回调函数的执行时机
      • post (默认值): 侦听器回调会在 DOM 更新之后执行。
      • pre: 与post相反,表示侦听器回调会在 DOM更新之前执行 的更新。这个选项适用于需要在 DOM 更新之前访问旧 DOM 的场景。
      • sync: 表示侦听器回调会在数据变化时立即同步执行。这通常会导致更高的性能开销,因为它会阻止其他任务的执行,直到侦听器回调完成。这个选项适用于需要立即响应数据变化,并且变化不频繁的场景。
    • onCleanup: 一个在侦听器停止侦听之前执行的函数(可以用来清除无效的副作用,例如等待中的异步请求。)
    • onTrack: 在依赖项被追踪时触发
    • onTrigger: 在依赖项的值发生变化并触发更新时触发
代码语言:javascript复制
import { ref, watch } from 'vue';

const data = ref(0);

watch(data, (newValue, oldValue, onCleanup) => {
  let timeoutId;
  onCleanup(() => {
    clearTimeout(timeoutId);
  });

  timeoutId = setTimeout(() => {
    console.log(`数据变化了,新值是 ${newValue}`);
  }, 1000);
}, { 
    immediate: true,
    onTrack: (dep, info) => {
        console.log(`在依赖项被追踪时触发:${dep}`);
     },
    onTrigger: (dep, newValue, oldValue) => {
        console.log(`依赖项触发更新:${dep},新值为 ${newValue},旧值为 ${oldValue}`);
    }
});

watchEffect()

自动追踪依赖:当你需要根据多个响应式数据的变化来执行某些操作时,不需要显式地指定依赖,watchEffect() 会自动追踪

立即执行:watchEffect()在组件初始化时会立即执行一次,确保依赖状态的最新值被正确应用。

无需关心具体响应式属性:当你不需要关心响应式数据具体是哪个属性变化,只是想在其变化时做一些事情时。

代码和效果图如下

代码语言:javascript复制
...
  <div>
    <input v-model="num1" type="number">
    <input v-model="num2" type="number">
    <h1>结果:{{ result }}</h1>
  </div>
...
...
import { ref, watchEffect } from 'vue'
let result = ref(0)
let num1 = ref(0)
let num2 = ref(0)
watchEffect(() => {
   result.value = num1.value   num2.value
});
...

组合函数(Composables)

组合函数是使用 Composition API 编写的函数,用于封装和复用逻辑。

生命周期钩子

Composition API 提供了与 Options API 对应的生命周期钩子函数,如 onMounted、onUnmounted 等。

逻辑分散:当组件变得复杂时,相关的逻辑可能分布在data、computed、methods、watch等不同的选项中,导致同一功能的代码片段被拆分到各个部分,增加了代码的查找和理解难度。

代码复用性差:Options API中,如果想要复用代码,通常需要创建混入(mixins),但混入会带来命名冲突、依赖关系不明确等问题,使得代码复用变得复杂且难以维护。

类型推断困难:在Vue 2中,Options API并不支持TypeScript的某些高级类型推断功能,这限制了在大型项目和复杂组件中使用TypeScript的能力。

组件组织混乱:随着组件的复杂性增加,组件的选项列表也会增长,这可能导致组件的选项组织混乱,难以清晰地识别和理解组件的结构。

响应式系统限制:Vue 2的响应式系统基于Object.defineProperty,对于数组或嵌套对象的处理不够高效,且不支持一些现代JavaScript语法(如 Proxy)提供的更强大和灵活的响应式特性。

模板逻辑复杂性:在Vue 2中,有时为了实现某些复杂的逻辑,需要在模板中写大量的逻辑代码,这违反了关注点分离的原则,使得模板变得复杂且难以维护。

组件测试困难:由于逻辑分散,组件测试变得复杂,测试每个功能时可能需要模拟多个不同的选项状态,增加了测试的难度和复杂性。,

是 Vue 3 引入的一种新的 API,旨在解决 Options API 在处理复杂组件逻辑时的局限性。它通过函数的方式来组织代码,使得逻辑更加模块化和可组合。

Vue 3 的 Composition API 是一种新的方式来定义和组织组件的逻辑,它提供了更灵活、可组合和可重用的代码结构。相比于 Vue 2 的 Options API,Composition API 使得在大型项目中管理复杂逻辑变得更加容易。以下是对 Vue 3 Composition API 的详细解释:

0 人点赞