Skip to content

组合式 API:setup()

注意

这篇文档所讲的是组件 setup 选项的使用方式。如果你正在搭配单文件组件使用组合式 API,建议使用更简洁好用的 <script setup> 语法糖。

setup() 这个钩子在以下情况下,作为组件中使用组合式 API 的入口。

  1. 不搭配构建步骤使用组合 API
  2. 在选项式 API 组件中集成基于组合式 API 的代码。

基本使用

我们可以使用 响应性 API 来声明响应式的状态,并可以将其在 setup() 函数的返回值对象上,以此暴露给模板。在其他的选项中,返回值对象上的属性同样会作为组件实例的属性:

<script>
import { ref } from 'vue'

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

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

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

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

注意从 setup 返回的 ref 在模板上访问时会 自动浅层解包 因此你无须再在模板中为它写 .value。当通过 this 访问时也会同样如此解包。

TIP

setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 null。你可以从选项式 API 中访问由组合式 API 暴露的值,但反过来则不行。

访问 Props

setup 函数的第一个参数是组件的 props。和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。

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

注意如果你从 props 对象上解构,被解构的变量将会丢失响应性。因此我们推荐通过 props.xxx 的形式来使用其中的属性。

如果你确实需要从 props 上解构,或者想要将某个 prop 传入到一个外部函数中但想保持响应性,那么你可以使用 toRefs() toRef() 这两个工具 API:

import { toRefs } from 'vue'

export default {
  setup(props) {
    // 将 `props` 转为一个其中全是 ref 的对象,然后解构
    const { title } = toRefs(props)
    // `title` 是一个追踪着 `props.title` 的 ref
    console.log(title.value)

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

Setup 的上下文

传入 setup 函数的第二个参数是一个 Setup 上下文 对象。上下文对象上暴露了其他一些在 setup 之中很有用的值:

export default {
  setup(props, context) {
    // Attributes(不是响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(不是响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 抛出事件(函数,等价于 $emit)
    console.log(context.emit)

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

该上下文对象是非响应式的,可以安全地解构:

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

attrsslots 都是有状态的对象,它们总会随着组件自身的变更而更新。这意味着你应当避免从它们之中解构,而是始终使用属性引用,例如 attrs.xslots.x 这样。此外还需注意,和 props 不同,attrsslots 上的属性都 不是 响应式的。如果你试图基于 attrsslots 来应用副作用,则应该放在生命周期钩子 onBeforeUpdate 之中。

暴露公共属性

expose 这个函数可以用于在父组件中通过模板 ref访问本组件时,显式地限制所暴露的属性:





 




 



export default {
  setup(props, { expose }) {
    // 这样会使得该组件处于 “关闭状态”
    // 即不向父组件暴露任何东西
    expose()

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

渲染函数的用法

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






 



import { h, ref } from 'vue'

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

返回一个渲染函数将会阻止我们返回其他东西。对组件内部来说,这应该不是个问题,但如果我们想通过模板 ref 将这个组件的方法暴露给父组件,那就有问题了。

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








 
 
 





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 上访问到。

组合式 API:setup() has loaded