现在位置: 首页 > Vue3 教程 > 正文

Vue3 组合式 API

Vue3 组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。

传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。

Vue3 使用组合式 API 的地方为 setup

在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式 API(Composition API) 允许我们编写更有条理的代码。

对比以下两端代码:

1、传统组件

2、组合式 API


setup 组件

setup() 函数在组件创建 created() 之前执行。

setup() 函数接收两个参数 props 和 context。

第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。

第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。

注意:在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

以下实例使用组合 API 定义一个计数器:

实例(src/APP.vue)

<template>
    <div>
        <p>计数器实例: {{ count }}</p>
        <input @click="myFn" type="button" value="点我加 1">
    </div>
</template>

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

export default {
    setup(){
        //定义初始值为0的变量,要使用ref方法赋值,直接赋值的话变量改变不会更新 UI
        let count = ref(0);

        // 定义点击事件 myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
       // 组件被挂载时,我们用 onMounted 钩子记录一些消息
        onMounted(() => console.log('component mounted!'));

        // 外部使用组合API中定义的变量或方法,在模板中可用。
        return {count,myFn} // 返回的函数与方法的行为相同
    }
}
</script>

在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:

import { ref } from 'vue'

let count = ref(0);

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。

在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。

实例

import { ref } from 'vue'

const counter = ref(0)

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

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

Vue 组合式 API 生命周期钩子

在 Vue2 中,我们通过以下方式实现生命周期钩子函数:

实例

export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },
  mounted() {
    console.log('V2 mounted!')
  }
};

在 Vue3 组合 API 中实现生命周期钩子函数可以在 setup() 函数中使用带有 on 前缀的函数:

实例

import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })
    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};

下表为 Options API 和 Composition API 之间的映射,包含如何在 setup () 内部调用生命周期钩子:

Vue2 Options-based APIVue Composition API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
errorCapturedonErrorCaptured

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

这些函数接受一个回调函数,当钩子被组件调用时将会被执行:

实例

setup() {
...
    // 组件被挂载时,我们用 onMounted 钩子记录一些消息
    onMounted(() => console.log('component mounted!'));
...
}

模板引用

在使用组合式 API 时,响应式引用和模板引用的概念是统一的。

为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:

实例

<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM 元素将在初始渲染后分配给 ref
        console.log(root.value) // <div>This is a root element</div>
      })

      return {
        root
      }
    }
  }
</script>

以上实例中我们在渲染上下文中暴露 root,并通过 ref="root",将其绑定到 div 作为其 ref。

作为模板使用的 ref 的行为与任何其他 ref 一样:它们是响应式的,可以传递到 (或从中返回) 复合函数中。

v-for 中的用法

组合式 API 模板引用在 v-for 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理:

实例

<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // 确保在每次更新之前重置ref
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>

侦听模板引用

侦听模板引用的变更可以替代前面例子中演示使用的生命周期钩子。

但与生命周期钩子的一个关键区别是,watch() 和 watchEffect() 在 DOM 挂载或更新之前运行会有副作用,所以当侦听器运行时,模板引用还未被更新。

实例

<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        // 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
        console.log(root.value) // => null
      })

      return {
        root
      }
    }
  }
</script>

因此,使用模板引用的侦听器应该用 flush: 'post' 选项来定义,这将在 DOM 更新后运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素。

实例

<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div>This is a root element</div>
      },
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>

API 手册

序号API 和 描述实例
1setup()
组件的组合式 API 中的入口函数,组件实例化时调用,用于定义响应式数据和方法。
export default { setup() { const count = ref(0); return { count }; } }
2ref()
创建一个响应式引用,用于包装基本类型或对象。
const count = ref(0)
3computed()
创建一个基于其他响应式数据计算出的值。
const doubledCount = computed(() => count.value * 2)
4reactive()
创建一个响应式对象,适用于对象或数组。
const state = reactive({ count: 0 })
5readonly()
创建只读的响应式数据,禁止修改。
const state = readonly({ count: 0 })
6watchEffect()
创建响应式副作用,自动追踪与其相关的响应式依赖。
watchEffect(() => { console.log(count.value) })
7watchPostEffect()
在 DOM 更新后触发的副作用。
watchPostEffect(() => { console.log('Post effect') })
8watchSyncEffect()
在响应式依赖变化时同步执行副作用。
watchSyncEffect(() => { console.log('Synchronized effect') })
9watch()
用于观察响应式数据的变化,可以指定特定的响应式数据或计算属性。
watch(count, (newCount) => { console.log(newCount) })
10onWatcherCleanup()
清理监听器,在观察的数据不再需要时调用。
watch(count, () => {}, onWatcherCleanup(() => { console.log('cleaned up') }))
11isRef()
检查一个值是否是 ref 类型。
isRef(count)
12unref()
解包一个 ref 的值。
unref(count)
13toRef()
创建一个从对象中提取的响应式引用。
const countRef = toRef(state, 'count')
14toValue()
解包 refreactive 类型的值。
toValue(count)
15toRefs()
将响应式对象的每个属性转换为独立的 ref
const { count } = toRefs(state)
16isProxy()
检查一个对象是否是 Vue 的代理对象。
isProxy(state)
17isReactive()
检查一个对象是否是响应式对象。
isReactive(state)
18isReadonly()
检查一个对象是否是只读的响应式对象。
isReadonly(state)
19shallowRef()
创建一个浅层的 ref,仅使对象的第一层属性响应式。
const state = shallowRef({ count: 0 })
20triggerRef()
强制触发 ref 的更新。
triggerRef(count)
21customRef()
创建一个自定义的响应式引用,允许开发者自定义 getter 和 setter。
const count = customRef((track, trigger) => { let value = 0; return { get: () => value, set: (val) => { value = val; trigger() } } })
22shallowReactive()
创建一个浅层响应式对象,只有对象的第一层属性是响应式的。
const state = shallowReactive({ count: 0 })
23shallowReadonly()
创建一个浅层只读的响应式对象,只有对象的第一层属性是只读的。
const state = shallowReadonly({ count: 0 })
24toRaw()
获取一个代理对象的原始对象。
const rawState = toRaw(state)
25markRaw()
将对象标记为不可代理的对象,Vue 将跳过该对象的响应式处理。
const obj = markRaw({ count: 0 })
26effectScope()
创建一个新的 effect scope,用于管理副作用。
const scope = effectScope()
27getCurrentScope()
获取当前的 effect scope。
const scope = getCurrentScope()
28onScopeDispose()
在 effect scope 被销毁时执行清理函数。
onScopeDispose(() => { console.log('Scope disposed') })
29onMounted()
组件挂载完成时调用的生命周期钩子。
onMounted(() => { console.log('Component mounted') })
30onUpdated()
组件更新时调用的生命周期钩子。
onUpdated(() => { console.log('Component updated') })
31onUnmounted()
组件卸载时调用的生命周期钩子。
onUnmounted(() => { console.log('Component unmounted') })
32onBeforeMount()
组件挂载前调用的生命周期钩子。
onBeforeMount(() => { console.log('Before mount') })
33onBeforeUpdate()
组件更新前调用的生命周期钩子。
onBeforeUpdate(() => { console.log('Before update') })
34onBeforeUnmount()
组件卸载前调用的生命周期钩子。
onBeforeUnmount(() => { console.log('Before unmount') })
35onErrorCaptured()
捕获组件树中未处理的错误。
onErrorCaptured((err, instance, info) => { console.error(err) })
36onRenderTracked()
组件渲染过程中,响应式数据被追踪时触发的钩子。
onRenderTracked((event) => { console.log(event) })
37onRenderTriggered()
组件渲染过程中,响应式数据发生变化时触发的钩子。
onRenderTriggered((event) => { console.log(event) })
38onActivated()
在组件被激活时调用的生命周期钩子(用于 keep-alive 组件)。
onActivated(() => { console.log('Component activated') })
39onDeactivated()
在组件被停用时调用的生命周期钩子(用于 keep-alive 组件)。
onDeactivated(() => { console.log('Component deactivated') })
40onServerPrefetch()
组件在服务器端渲染时预取数据的钩子。
onServerPrefetch(async () => { await fetchData() })
41provide()
向下传递依赖给后代组件。
provide('key', value)
42inject()
从父级组件或祖先组件注入依赖。
const value = inject('key')
43hasInjectionContext()
检查当前是否有注入上下文。
hasInjectionContext()
44useAttrs()
获取组件的所有属性,包括传递给组件的 HTML 特性。
const attrs = useAttrs()
45useSlots()
获取组件的插槽内容。
const slots = useSlots()
46useModel()
获取当前组件的 v-model 绑定的值。
const modelValue = useModel()
47useTemplateRef()
获取模板中元素的引用。
const templateRef = useTemplateRef('elementId')
48useId()
获取一个唯一的标识符。
const uniqueId = useId()