Skip to content

Композиційний АРІ: setup()

Основне використання

Хук setup() служить точкою входу для використання Композиційного API у компонентах у наступних випадках:

  1. Використання Композиційного API без етапу збірки;
  2. Інтеграція коду на основі Композиційного API в Опційний API компонента.

Примітка

Якщо ви використовуєте композиційний API з однофайловими компонентами, настійно рекомендується <script setup> для більш стислого та ергономічного синтаксису.

Ми можемо оголосити реактивний стан за допомогою API Реактивності й представити його шаблону, повернувши об’єкт із setup(). Властивості повернутого об'єкта також будуть доступні в екземплярі компонента (якщо використовуються інші параметри):

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

export default {
  setup() {
    const count = ref(0)

    // представити до шаблону та інших параметрів API хуків
    return {
      count
    }
  },

  mounted() {
    console.log(this.count) // 0
  }
}
</script>

<template>
  <button @click="count++">{{ count }}</button>
</template>

Зауважте, що референції, повернуті з setup, автоматично неглибоко розгортаються під час доступу в шаблоні, тому вам не потрібно використовувати .value під час доступу до них. Вони розгортаються таким же чином, коли до них звертаються через this.

Сам setup() не має доступу до екземпляра компонента - тому this матиме значення undefined всередині setup(). Ви можете отримати доступ до значень представленого Композиційним API з Опційного API, але не навпаки.

setup() має повертати об’єкт синхронно. Єдиний випадок, коли можна використовувати async setup(), це коли компонент є нащадком компонента Suspense.

Доступ до реквізитів

Першим аргументом у функції setup є аргумент props. Так само як і очікувалося в стандартному компоненті, props всередині функції setup є реактивними та оновлюються, коли надходять нові реквізити.

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

Зауважте, що якщо ви деструктуруєте об’єкт props, то деструктуровані змінні втратять реактивність. Тому рекомендується завжди звертатися до реквізитів у формі props.xxx.

Якщо вам справді потрібно деструктурувати реквізити або передати реквізит у зовнішню функцію, зберігаючи реактивність, ви можете зробити це за допомогою API утиліт toRefs() і toRef() :

js
import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // перетворюємо `props` на об'єкт посилань, потім деструктуруємо
    const { title } = toRefs(props)
    // `title` — це посилання, яке відстежує `props.title`
    console.log(title.value)

    // АБО перетворити одну властивість `props` на посилання
    const title = toRef(props, 'title')
  }
}

Контекст setup

Другим аргументом, який передається до функції setup, є об’єкт Контекст setup. Об’єкт контексту надає інші значення, які можуть бути корисними всередині 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)
  }
}

Об’єкт контексту не реактивний і може бути безпечно деструктурований:

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

attrs і slots — це об'єкти зі збереженням стану, які завжди оновлюються, коли оновлюється сам компонент. Це означає, що вам слід уникати їх деструктуризації та завжди посилатися на властивості як attr.x або slots.x. Також зауважте, що, на відміну від props, властивості attrs і slots не реактивні. Якщо ви маєте намір застосувати побічні ефекти на основі змін до attrs або slots, вам слід зробити це всередині хука життєвого циклу 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 буде доступний у батьківському компоненті через посилання шаблону.

Композиційний АРІ: setup() has loaded