setup() 是 Vue 3 组件选项 API 中的一个新选项。它是 Composition API 的入口点,在组件被创建之前执行,用于初始化状态、计算属性和方法,并返回在模板中使用的响应式引用。

setup 方法

为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在Vue 3的组件中,我们将此位置称为setup方法,如示例代码所示。

<div id="app">
  <component-b user="John" />
</div>
const componentB = {
  props: {
    user: {
      type: String,
      required: true
    }
  },
  template:'<div></div>',
  setup(props,context) {
    console.log(props.user) // 打印'John'
    return {} // 这里返回的任何内容都可以用于组件的其余部分
  }
}
Vue.createApp({
  components: {
    'component-b': componentB
  }
}).mount("#app")

setup方法参数

setup方法中接收两个参数,第一个参数式props,它和之前讲解组件通信中的props一样,可以接收到父组件传递的数据,同样,如果props是一个动态值,那么它就是响应式的,会随着父组件的改变而更新。

但是,因为props是响应式的,你不能使用ES6解构,它会消除prop的响应性。如果需要解构prop,可以在setup方法中使用toRefs函数来完成此操作,如下代码所示:

setup(props,context) {
    const { user } = Vue.toRefs(props)
    console.log(user.value) // 打印'John'
}

注意,如果是采用npm来管理的项目,可以采用如下import方式引入toRefs,包括后续的Composition API相关的方法:

import { toRefs } from 'vue'

如果user是可选的prop,则传入的props中可能没有user。在这种情况下,需要使用toRef替代它,代码如下:

setup(props,context) {
    const { user } = Vue.toRef(props,'user')
    console.log(user.value) // 打印'John'
}

setup方法的第二个参数是context对象,context是一个普通的JavaScript对象,它暴露组件的三个属性,分别是attrs,slots,emit,并且由于是普通的JavaScript对象,可以之间采用ES6解构,如示例代码所示。

<div id="app">
  <component-b attrone="one" @emitcallback="emitcallback">
    <template v-slot:slotone>
      <span>slot</span>
    </template>
  </component>
</div>
const componentB = {

  template:'<div></div>',
  setup(props, { attrs, slots, emit }) {

    // Attribute (非响应式对象)
    console.log(attrs) // 打印 { attrone: 'one' } 相当于this.$attrs

    // 插槽 (非响应式对象)
    console.log(slots.slotone) // 打印{ slotone: function(){} } 相当于this.$slots

    // 触发事件 (方法)
    console.log(emit) // 可调用emit('emitcallback')相当于this.$emit

  },

}
const vm = Vue.createApp({
  components: {
    'component-b': componentB
  },
  methods:{
    emitcallback(){
      console.log('emitcallback')
    }
  }
}).mount("#app")

其中,attrs对象是父组件传递给子组件且不在props中定义的的静态数据,它是非响应式的,相当于在没有使用setup方法时之外调用的this.$attrs效果。

slots对象主要是父组件传递的插槽内容,注意v-slot:slotone需要配置插槽名字,这样slots才能接收到,它是非响应式的,相当于在没有使用setup方法时之外调用的this.$slots效果。

emit对象主要用来和父组件通信,相当于在没有使用setup方法时之外调用的this.$emit效果。

setup方法结合模板使用

如果setup方法返回一个对象,那么该对象的属性以及传递给setup的props参数中的属性就都可以在模板template中访问到,如示例代码所示。

<div id="app">
  <component-b user="John" />
</div>
const componentB = {
  props: {
    user: {
      type: String,
      required: true
    }
  },
  template:'<div>{{user}} {{person.name}}</div>',
  setup(props) {

    const person = Vue.reactive({ name: 'Son' })
    // 暴露给 template
    return {
        person
    }
  },

}
Vue.createApp({
  components: {
    'component-b': componentB
  }
}).mount("#app")

注意,props中的数据我们不必在setup中返回,Vue会自动的暴露给模板template中使用。