# Функция setup

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

Подразумевается, что уже изучили и разобрались с разделами Введение в Composition API и Основы реактивности. Если нет — прочитайте их сначала.

# Аргументы

При использовании функции setup она будет получать два аргумента:

  1. props — входные параметры
  2. context — контекст

Разберёмся подробнее с тем, как можно использовать каждый из аргументов.

# Входные параметры

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

// MyBook.vue

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

ВНИМАНИЕ

Поскольку props реактивны, то нельзя использовать деструктуризацию ES6, потому что это уберёт реактивность со входных параметров.

Если необходимо выполнить деструктуризацию входных параметров, то это можно сделать с помощью toRefs внутри функции setup:

// MyBook.vue

import { toRefs } from 'vue'

setup(props) {
  const { title } = toRefs(props)

  console.log(title.value)
}
1
2
3
4
5
6
7
8
9

Если, например, title необязательный входной параметр, то он может отсутствовать в props. В этом случае toRefs не создаёт ссылку для title и вместо него потребуется воспользоваться toRef:

// MyBook.vue

import { toRef } from 'vue'

setup(props) {
  const title = toRef(props, 'title')

  console.log(title.value)
}
1
2
3
4
5
6
7
8
9

# Контекст

Второй аргумент, передаваемый в функцию setup, будет контекст context. Это обычный объект JavaScript через который предоставляется доступ к другим значениям, полезным внутри setup:

// MyBook.vue

export default {
  setup(props, context) {
    // Атрибуты (нереактивный объект, эквивалент $attrs)
    console.log(context.attrs)

    // Слоты (нереактивный объект, эквивалент $slots)
    console.log(context.slots)

    // Генерация событий (функция, эквивалент $emit)
    console.log(context.emit)

    // Объявленные публичные свойства (функция)
    console.log(context.expose)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Так как объект context будет обычным объектом JavaScript (т.е. он нереактивный), то для context можно спокойно использовать деструктуризацию ES6.

// MyBook.vue
export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}
1
2
3
4
5
6

Свойства attrs и slots — объекты с состоянием, которые всегда будут обновляться при обновлении самого компонента. Это значит, что следует избегать деструктуризации для них и всегда ссылаться на свойства как attrs.x или slots.x. Обратите внимание также, что в отличие от props, свойства attrs и slots НЕ РЕАКТИВНЫ. Если необходимо применять побочные эффекты, основанные на изменениях attrs или slots, то следует делать это внутри жизненного цикла onBeforeUpdate.

Роль expose рассмотрим в ближайшее время.

# Доступ к свойствам компонента

При выполнении setup ещё не будет создан экземпляр компонента. Поэтому можно получить доступ только к следующим свойствам:

  • props
  • attrs
  • slots
  • emit

Другими словами, не получится обратиться к следующим опциям компонента:

  • data
  • computed
  • methods
  • refs (ссылки на элементы шаблона)

# Использование в шаблонах

Если функция setup возвращает объект, то его свойства также будут доступны в шаблоне компонента, как и свойства props, передаваемые в setup:

<!-- MyBook.vue -->
<template>
  <div>{{ collectionName }}: {{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'

  export default {
    props: {
      collectionName: String
    },
    setup(props) {
      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
22
23
24

Запомните, возвращаемые из setup refs при обращениях в шаблоне автоматически неглубоко разворачиваются, поэтому указывать .value в шаблоне не требуется.

# Использование в render-функциях

Функция setup может также возвращать 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

Возвращение render-функции, не позволяет возвращать что-либо ещё. Внутри компонента это не проблема, но могут быть сложности, если нужно объявить методы этого компонента, которые могут использоваться родительским компонентом через ссылки в шаблоне.

Эту проблему можно решить с помощью вызова 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)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Теперь метод increment доступен в родительском компоненте через ссылку в шаблоне.

# Использование this

Внутри setup() использование this не будет ссылкой на текущий активный экземпляр. Так как setup() вызывается до разрешения других опций компонента, то поведение this внутри setup() будет несколько отличаться от this в других опциях. Это может привести к путанице при использовании setup() совместно с другими Options API.