# Composition

# mixins

  • 型: Array<Object>

  • 詳細:

    mixins オプションは、ミックスインオブジェクトの配列を受け入れます。これらのミックスインオブジェクトは、通常のインスタンスオブジェクトと同じようにインスタンスオプションを含めることができ、それらは一定のオプションをマージするロジックを使って、最終的なオプションに対してマージされます。例えば、あなたのミックスインが created フックを含み、コンポーネントそれ自身にも created フックがある場合、両方の関数が呼び出されます。

    ミックスインフックは提供された順番に呼び出され、コンポーネント自身のフックよりも前に呼び出されます。

    INFO

    Vue 2 では、コンポーネントロジックの再利用可能なチャンクを作成するための主要なメカニズムがミックスインでした。Vue 3 では、引き続きミックスインがサポートされていますが、コンポーネント間でコードの再利用するには Composition API が推奨されています。

  • 例:

    const mixin = {
      created() {
        console.log(1)
      }
    }
    
    createApp({
      created() {
        console.log(2)
      },
      mixins: [mixin]
    })
    
    // => 1
    // => 2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 参照: ミックスイン

# extends

  • 型: Object

  • 詳細:

    あるコンポーネントを別のコンポーネントに拡張して、そのコンポーネントオプションを継承することができます。

    実装の観点からは、extendsmixins とほとんど同じです。extends で指定されたコンポーネントは、最初のミックスインであるかのように扱われます。

    しかし、extendsmixins は異なる意図を表現します。mixins オプションは主に機能のチャンクを構成するために使われ、extends は主に継承に関係しています。

    mixins と同様に、どのオプションも関連するマージ戦略を使ってマージされます。

  • 例:

    const CompA = { ... }
    
    const CompB = {
      extends: CompA,
      ...
    }
    
    1
    2
    3
    4
    5
    6

# provide / inject

  • 型:

    • provide: Object | () => Object
    • inject: Array<string> | { [key: string]: string | Symbol | Object }
  • 詳細:

    この一組のオプションを一緒に使うことで、コンポーネント階層の深さに関わらず、それらが同じ親チェーンにある限り、祖先のコンポーネントがその子孫コンポーネントすべての依存オブジェクトの注入役として機能することができます。React に慣れている方には、これは React の context の機能と非常によく似ています。

    provide オプションは、オブジェクトまたはオブジェクトを返す関数でなければなりません。このオブジェクトは、その子孫に注入可能なプロパティを含みます。このオブジェクトのキーには ES2015 の Symbol を使うことができますが、ネイティブで SymbolReflect.ownKeys をサポートしている環境でのみ有効です。

    inject オプションは、次のいずれかでなければなりません:

    • 文字列の配列、もしくは
    • キーがローカルのバインディング名で、値が次のいずれかであるオブジェクト:
      • 利用可能な注入オブジェクトを検索するためのキー(文字列または Symbol)、または
      • オブジェクトが:
        • from プロパティは利用可能な注入オブジェクトを検索するためのキー(文字列または Symbol)、そして
        • default プロパティはフォールバック値として使われます

    注意: provideinject のバインディングはリアクティブではありません。これは意図的なものです。ただし、あなたがリアクティブなオブジェクトを渡した場合、そのオブジェクトのプロパティはリアクティブなままです。

  • 例:

    // 'foo' を提供している親コンポーネント
    const Provider = {
      provide: {
        foo: 'bar'
      }
      // ...
    }
    
    // 'foo' を注入している子コンポーネント
    const Child = {
      inject: ['foo'],
      created() {
        console.log(this.foo) // => "bar"
      }
      // ...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    ES2015 の Symbol で、provide 関数と inject オブジェクトを使います:

    const s = Symbol()
    
    const Provider = {
      provide() {
        return {
          [s]: 'foo'
        }
      }
    }
    
    const Child = {
      inject: { s }
      // ...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    注入された値をプロパティのデフォルトとして使います:

    const Child = {
      inject: ['foo'],
      props: {
        bar: {
          default() {
            return this.foo
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    注入された値をデータプロパティの登録に使います:

    const Child = {
      inject: ['foo'],
      data() {
        return {
          bar: this.foo
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    注入はデフォルト値で任意にできます:

    const Child = {
      inject: {
        foo: { default: 'foo' }
      }
    }
    
    1
    2
    3
    4
    5

    別の名前のプロパティから注入する必要がある場合は、from を使って元のプロパティを指定します:

    const Child = {
      inject: {
        foo: {
          from: 'bar',
          default: 'foo'
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    プロパティのデフォルトと同じように、プリミティブ値以外はファクトリ関数を使う必要があります:

    const Child = {
      inject: {
        foo: {
          from: 'bar',
          default: () => [1, 2, 3]
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 参照: Provide / Inject

# setup

  • 型: Function

setup 関数は、新しいコンポーネントオプションです。この関数は、コンポーネント内で Composition API を使うためのエントリポイントになります。

  • 呼び出しのタイミング

    setup は、コンポーネントインスタンスが作成されたとき、初期プロパティの解決の直後に呼び出されます。ライフサイクル的には、beforeCreate フックの前に呼び出されます。

  • テンプレートでの利用

    setup がオブジェクトを返す場合、そのオブジェクトのプロパティはコンポーネントのテンプレートのレンダリングコンテキストにマージされます:

    <template>
      <div>{{ count }} {{ object.foo }}</div>
    </template>
    
    <script>
      import { ref, reactive } from 'vue'
    
      export default {
        setup() {
          const count = ref(0)
          const object = reactive({ foo: 'bar' })
    
          // expose to template
          return {
            count,
            object
          }
        }
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    setup から返された refs は、テンプレート内でアクセスする時に自動的にアンラップされるので、テンプレートで .value を使う必要はありません。

  • Render 関数 / JSX での利用

    setup は、同じスコープで宣言されたリアクティブな状態を直接使える Render 関数を返すこともできます:

    import { h, ref, reactive } from 'vue'
    
    export default {
      setup() {
        const count = ref(0)
        const object = reactive({ foo: 'bar' })
    
        return () => h('div', [count.value, object.foo])
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 引数

    この関数は、その第 1 引数として解決されたプロパティを受け取ります:

    export default {
      props: {
        name: String
      },
      setup(props) {
        console.log(props.name)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    この props オブジェクトはリアクティブです。つまり、新しいプロパティが渡されると更新され、watchEffectwatch を使って監視と反応をすることができます:

    export default {
      props: {
        name: String
      },
      setup(props) {
        watchEffect(() => {
          console.log(`name is: ` + props.name)
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    しかし、props オブジェクトのリアクティビティが失われるため、そのオブジェクトを分割してはいけません:

    export default {
      props: {
        name: String
      },
      setup({ name }) {
        watchEffect(() => {
          console.log(`name is: ` + name) // リアクティブではないでしょう!
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    props オブジェクトは、開発中のユーザランドのコードにとってはイミュータブルです(ユーザのコードがそれを変更しようとすると警告を表示します)。

    第 2 引数は、setup で便利だと思われる様々なオブジェクトや関数を公開するコンテキストオブジェクトを提供します:

    const MyComponent = {
      setup(props, context) {
        context.attrs
        context.slots
        context.emit
        context.expose
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    attrsslotsemit はそれぞれ、インスタンスプロパティの $attrs$slots$emit と同じです。

    attrsslots は内部コンポーネントインスタンスの対応する値へのプロキシです。これは更新後も常に最新の値が公開されることを保証するので、古くなった参照へのアクセスを心配することなく、構造を変更することができます:

    const MyComponent = {
      setup(props, { attrs }) {
        // 後の段階で呼び出されるはずの関数
        function onClick() {
          console.log(attrs.foo) // 最新の参照が保証されている
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    Vue 3.2 で追加された expose は、特定のプロパティをパブリックなコンポーネントインスタンスを介して公開することができる関数です。デフォルトでは、refs や $parent$root を使って取得したパブリックなインスタンスは、テンプレートが使う内部インスタンスと同じです。expose を呼び出すと、指定したプロパティを持つ別のパブリックなインスタンスが作成されます:

    const MyComponent = {
      setup(props, { expose }) {
        const count = ref(0)
        const reset = () => count.value = 0
        const increment = () => count.value++
    
        // reset だけが、例えば $refs を介して、外部から利用できるようになります
        expose({
          reset
        })
    
        // 内部的には、テンプレートは count と increment にアクセスできます
        return { count, increment }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    props がコンテキストに含まれる代わりに、別の第 1 引数として置かれている理由はいくつかあります:

    • コンポーネントが props を使うことが、他のプロパティよりもずっと一般的であること、そしてコンポーネントが props のみを扱うことがとても頻繁にあることです。

    • props を別の引数として持つことで、コンテキストの他のプロパティの型を混乱させることなく、個別に入力することが楽になります。また TSX のサポートで setuprender、単純な関数コンポーネントを通して、一貫した定義を守ることができます。

  • 参照: Composition API