# Composition API

В этом разделе в примерах кода используется синтаксис однофайловых компонентов

# setup

Опция компонента, которая является стартовой точкой для Composition API и выполняется перед созданием компонента, после разрешения входных параметров props.

  • Аргументы:

    • {Data} props — входные параметры
    • {SetupContext} context — контекст

    Аналогично this.$props при использовании Options API, объект props будет содержать только явно объявленные входные параметры. Кроме того, все объявленные ключи входных параметров будут присутствовать в объекте props, независимо от того, передавались ли родительским компонентом или нет. Отсутствующие опциональные входные параметры будут иметь значение undefined.

    Если требуется проверить отсутствие необязательного входного параметра, то можно использовать Symbol в качестве значения по умолчанию:

     



     


     





    const isAbsent = Symbol()
    
    export default {
      props: {
        foo: { default: isAbsent }
      },
      setup(props) {
        if (props.foo === isAbsent) {
          // foo не был указан.
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • Типизация:

    interface Data {
      [key: string]: unknown
    }
    
    interface SetupContext {
      attrs: Data
      slots: Slots
      emit: (event: string, ...args: unknown[]) => void
      expose: (exposed?: Record<string, any>) => void
    }
    
    function setup(props: Data, context: SetupContext): Data
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    Совет

    Необходимо использовать defineComponent чтобы получить вывод типов для аргументов, передаваемых в setup().

  • Пример

    С использованием шаблона:

    <!-- MyBook.vue -->
    <template>
      <div>{{ readersNumber }} {{ book.title }}</div>
    </template>
    
    <script>
      import { ref, reactive } from 'vue'
    
      export default {
        setup() {
          const readersNumber = ref(0)
          const book = reactive({ title: 'Руководство по Vue 3' })
    
          // объявление для шаблона
          return {
            readersNumber,
            book
          }
        }
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    С использованием render-функции:

    // MyBook.vue
    
    import { h, ref, reactive } from 'vue'
    
    export default {
      setup() {
        const readersNumber = ref(0)
        const book = reactive({ title: 'Руководство по Vue 3' })
    
        // Обратите внимание, здесь потребуется явно использовать значение ref
        return () => h('div', [readersNumber.value, book.title])
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    Если возвращать render-функцию, то не получится вернуть и другие свойства. Поэтому если потребуется объявить свойства, чтобы можно было получать доступ к ним извне, например, через ref из родителя, то можно воспользоваться expose:

    // MyBook.vue
    
    import { h } from 'vue'
    
    export default {
      setup(props, { expose }) {
        const reset = () => {
          // Какая-то логика для сброса
        }
    
        // Expose может быть вызван только один раз.
        // Если необходимо объявить несколько свойств, то все они
        // должны быть указаны в объекте, передаваемом в expose.
        expose({
          reset
        })
    
        return () => h('div')
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  • См. также: Composition API setup

# Хуки жизненного цикла

Хуки жизненного цикла можно регистрировать через явно импортируемые функции onX:

 



 


 


 





import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('примонтирован!')
    })
    onUpdated(() => {
      console.log('обновлён!')
    })
    onUnmounted(() => {
      console.log('размонтирован!')
    })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Функции хуков жизненного цикла могут использоваться только синхронно внутри setup(), поскольку они полагаются на внутреннее локальное состояние для определения текущего активного экземпляра (экземпляра компонента чей setup() сейчас вызывается). Их вызов без текущего активного экземпляра приведёт к ошибке.

Контекст экземпляра компонента также устанавливается во время синхронного выполнения хуков жизненного цикла. В результате, методы-наблюдатели и вычисляемые свойства созданные синхронно внутри хуков жизненного цикла будут также автоматически уничтожаться при размонтировании компонента.

  • Соответствие между хуками Options API и Composition API

    • beforeCreate -> используйте setup()
    • created -> используйте setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeUnmount -> onBeforeUnmount
    • unmounted -> onUnmounted
    • errorCaptured -> onErrorCaptured
    • renderTracked -> onRenderTracked
    • renderTriggered -> onRenderTriggered
    • activated -> onActivated
    • deactivated -> onDeactivated
  • См. также: Composition API. Хуки жизненного цикла

# Provide / Inject

Функции provide и inject предоставляют возможность внедрения зависимостей. Они могут быть вызваны только во время setup() с текущим активным экземпляром.

  • Типизация:

    interface InjectionKey<T> extends Symbol {}
    
    function provide<T>(key: InjectionKey<T> | string, value: T): void
    
    // без значения по умолчанию
    function inject<T>(key: InjectionKey<T> | string): T | undefined
    
    // со значением по умолчанию
    function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
    
    // с функцией-фабрикой
    function inject<T>(
      key: InjectionKey<T> | string,
      defaultValue: () => T,
      treatDefaultAsFactory: true
    ): T
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    Vue предоставляет интерфейс InjectionKey — общий тип, расширяющий Symbol. Его можно использовать для синхронизации типа внедряемого значения между провайдером и получателем:

    import { InjectionKey, provide, inject } from 'vue'
    
    const key: InjectionKey<string> = Symbol()
    
    provide(key, 'foo') // указание не-строчного значения приведёт к ОШИБКЕ
    
    const foo = inject(key) // тип foo: string | undefined
    
    1
    2
    3
    4
    5
    6
    7

    При использовании строковых ключей или не-типизированных Symbol, тип внедряемого значения необходимо объявить явно:

    const foo = inject<string>('foo') // string | undefined
    
    1
  • См. также:

# getCurrentInstance

Метод getCurrentInstance позволяет получить доступ к внутреннему экземпляру компонента.

ВНИМАНИЕ

Функция getCurrentInstance предоставлена только для продвинутых случаев, обычно для библиотек. Использование getCurrentInstance в коде приложения настоятельно не рекомендуется. НЕ ИСПОЛЬЗУЙТЕ его в качестве варианта для получения эквивалента this в Composition API.

 



 

 



import { getCurrentInstance } from 'vue'

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance()

    internalInstance.appContext.config.globalProperties // доступ к globalProperties
  }
}
1
2
3
4
5
6
7
8
9

Метод getCurrentInstance работает только во время setup или хуков жизненного цикла

При использовании вне setup или хуков жизненного цикла вызывайте getCurrentInstance() в setup и используйте вместо него экземпляр.

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance() // работает

    const id = useComponentId() // работает

    const handleClick = () => {
      getCurrentInstance() // НЕ БУДЕТ РАБОТАТЬ
      useComponentId() // НЕ БУДЕТ РАБОТАТЬ

      internalInstance // работает
    }

    onMounted(() => {
      getCurrentInstance() // работает
    })

    return () =>
      h(
        'button',
        {
          onClick: handleClick
        },
        `uid: ${id}`
      )
  }
}

// также работает при вызове в функции композиции
function useComponentId() {
  return getCurrentInstance().uid
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32