本文へジャンプ

Composition API: setup()

基本的な使い方

setup() フックは、次のような場合にコンポーネントで Composition API を使用するのためのエントリーポイントとして機能します:

  1. ビルドステップなしでの Composition API の使用
  2. Options API コンポーネント内の Composition API に基づくコードの統合

Note

単一ファイルコンポーネントで 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() 自体はコンポーネントインスタンスにアクセスできません。- thissetup() 内では undefined 値を持ちます。Options API から Composition API で公開された値にアクセスすることができますが、その逆はできません。

setup()同期的にオブジェクトを返さなければなりません。async setup() が使用できるのは、そのコンポーネントが Suspense コンポーネントの子孫であるときだけです。

props へのアクセス

setup 関数の第 1 引数は props 引数です。標準コンポーネントで期待するように、setup 関数内の props はリアクティブで、新しい props が渡されたら更新されます。

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

もし、props オブジェクトを分割代入する場合は、分割代入された変数はリアクティビティーを失うことに注意してください。 そのため、常に props.xxx の形で props にアクセスすることを推奨します。

もし、本当に props を分割代入したり、もしくはリアクティビティーを保持しながら外部の関数に渡すことが必要なら、 ユーティリティー API である toRefs()toRef() を使用することで、行うことができます:

js
import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // `props` から refs オブジェクトへ変換し、分割代入を行います
    const { title } = toRefs(props)
    // `title` は `props.title` を追跡する ref です
    console.log(title.value)

    // もしくは、 `props` 内の単一プロパティから ref へ変換します
    const title = toRef(props, 'title')
  }
}

Setup Context

setup 関数へ渡される第 2 引数は Setup Context オブジェクトです。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)
  }
}

context オブジェクトはリアクティブではなく、安全に分割代入できます:

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

attrsslots はステートフルなオブジェクトです。コンポーネント自身が更新されたとき、常に更新されます。つまり、分割代入の使用を避け、attrs.xslots.x のようにプロパティを常に参照する必要があります。また、props とは異なり、attrsslots のプロパティは リアクティブではない ということに注意してください。もし、attrsslots の変更による副作用を適用したいのなら、onBeforeUpdate ライフサイクルフックの中で行うべきです。

パブリックプロパティの公開

expose は、親コンポーネントからテンプレート参照でコンポーネントインスタンスにアクセスする際に、公開するプロパティを明示的に制限するために使用することができる関数です:

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)
  }
}

レンダー関数を返すことで、他のものを返すことができなくなります。内部的には問題ありませんが、このコンポーネントのメソッドをテンプレート参照から親コンポーネントに公開したい場合には、問題となります。

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 メソッドは、親コンポーネントでテンプレート参照を介して利用可能になります。

Composition API: setup()が読み込まれました