Skip to content

Composition API:setup() ​

基本用法 ​

setup() 钩子在以下情况下充当组件中使用 Composition API 的入口点:

  1. 在不使用构建步骤的情况下使用 Composition API;
  2. 在 Options API 组件中集成基于 Composition API 的代码。

注意

如果你在单文件组件中使用 Composition API,强烈建议使用 <script setup>,以获得更简洁、更易用的语法。

我们可以使用 响应式 API 声明响应式状态,并通过从 setup() 返回一个对象将其暴露给模板。返回对象上的属性也会在组件实例上可用(如果使用了其他选项):

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

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

    // 暴露给模板和其他 Options API 钩子
    return {
      count
    }
  },

  mounted() {
    console.log(this.count) // 0
  }
}
</script>

<template>
  <button @click="count++">{{ count }}</button>
</template>

setup 返回的 ref 在模板中访问时会被自动浅层解包,因此访问它们时无需使用 .value。当在 this 上访问时,它们也会以相同方式被解包。

setup() 本身无法访问组件实例——在 setup() 内部,this 的值将是 undefined。你可以从 Options API 中访问通过 Composition API 暴露的值,但不能反过来。

setup() 应该同步返回一个对象。只有当组件是 Suspense 组件的后代时,才能使用 async setup()。

访问 Props ​

传递给 setup 函数的第一个参数是 props 参数。与标准组件中的预期一致,setup 函数内部的 props 是响应式的,并且在传入新的 props 时会更新。

js
export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

请注意,如果你解构了 props 对象,解构得到的变量将失去响应性。因此建议始终以 props.xxx 的形式访问 props。

如果你确实需要解构 props,或者需要在保持响应性的同时将某个 prop 传递给外部函数,可以使用 toRefs() 和 toRef() 工具 API:

js
import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // 将 `props` 转换为 refs 对象,然后再解构
    const { title } = toRefs(props)
    // `title` 是一个 ref,用来追踪 `props.title`
    console.log(title.value)

    // 或者,将 `props` 上的单个属性转换为 ref
    const title = toRef(props, 'title')
  }
}

Setup 上下文 ​

传递给 setup 函数的第二个参数是一个 Setup Context 对象。该上下文对象暴露了在 setup 内部可能有用的其他值:

js
export default {
  setup(props, context) {
    // 属性(非响应式对象,等同于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式对象,等同于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等同于 $emit)
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

上下文对象不是响应式的,因此可以安全地进行解构:

js
export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

attrs 和 slots 是有状态的对象,当组件本身更新时它们总会同步更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的形式引用属性。还要注意,与 props 不同,attrs 和 slots 的属性不是响应式的。如果你打算根据 attrs 或 slots 的变化应用副作用,你应该在 onBeforeUpdate 生命周期钩子中进行。

暴露公共属性 ​

expose 是一个函数,可用于在父组件通过 模板 ref 访问组件实例时,显式限制所暴露的属性:

js
export default {
  setup(props, { expose }) {
    // 将实例“关闭”-
    // 即不向父组件暴露任何内容
    expose()

    const publicCount = ref(0)
    const privateCount = ref(0)
    // 有选择地暴露局部状态
    expose({ count: publicCount })
  }
}

与渲染函数一起使用 ​

setup 也可以返回一个渲染函数,它可以直接使用在同一作用域中声明的响应式状态:

js
import { h, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return () => h('div', count.value)
  }
}

返回渲染函数会阻止我们再返回任何其他内容。从内部来说这不会有问题,但如果我们想通过模板 ref 将此组件的方法暴露给父组件,这就可能成为问题。

我们可以通过调用 expose() 来解决这个问题:

js
import { h, ref } from 'vue'

export default {
  setup(props, { expose }) {
    const count = ref(0)
    const increment = () => ++count.value

    expose({
      increment
    })

    return () => h('div', count.value)
  }
}

这样,increment 方法就可以通过模板 ref 在父组件中使用。

Composition API:setup() has loaded