Vue3 Composition API

Vue3 Composition API в основном используется для повышения переиспользуемости логики кода в больших компонентах.

По мере роста сложности бизнеса традиционные компоненты продолжают увеличиваться в объёме кода, и всю логику кода становится трудно читать и понимать.

Место, где Vue3 использует Composition API, — это функция setup().setup。

В setup мы можем группировать части кода по логическим задачам, затем извлекать фрагменты логики и делиться ими с другими компонентами. Поэтому Composition API позволяет нам писать более организованный код.

Сравните следующие два фрагмента кода:

1. Традиционные компоненты

2. Composition API


Компонент setup

Функция setup() выполняется до создания компонента (created()).

Функция setup() принимает два параметра: props и context.

Первый параметр, props, является реактивным и будет обновляться при передаче новых props.

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

Примечание:В setup следует избегать использования this, потому что он не найдёт экземпляр компонента. Вызов setup происходит до того, как будут разрешены свойства data, вычисляемые свойства или методы, поэтому к ним нельзя получить доступ в setup.

В следующем примере используется Composition API для определения счётчика:

Пример (src/APP.vue)

<template>
    <div>
        <p>Пример счетчика: {{ count }}</p>
        <input @click="myFn" type="button" value="Нажми меня, чтобы добавить 1">
    </div>
</template>

<script>
import {ref, onMounted} from 'vue';

export default {
    setup(){
// Определите переменную с начальным значением 0. Используйте метод ref для присваивания; если присвоить напрямую, изменения переменной не будут обновлять UI.
        let count = ref(0);

// Определите событие клика myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
// Когда компонент смонтирован, мы используем хук onMounted, чтобы записать несколько сообщений
        onMounted(() => console.log('компонент смонтирован!'));

// Переменные или методы, определённые с помощью Composition API, доступны в шаблоне.
return {count, myFn} // Возвращаемые функции ведут себя так же, как методы
    }
}
</script>

В Vue 3.0 мы можем использовать новую функцию ref, чтобы любая реактивная переменная работала где угодно, как показано ниже:

import { ref } from 'vue'

let count = ref(0);

ref()Функция создаёт реактивный объект данных на основе заданного значения. Возвращаемое значение — это объект, содержащий только одно.value.value свойство.

В функции setup() реактивные данные, созданные с помощью ref(), возвращают объект, поэтому вам нужно использовать.value.value для доступа.

Пример

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

Хуки жизненного цикла Vue Composition API

В Vue2 мы реализуем функции-хуки жизненного цикла следующим образом:

Пример

export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },
  mounted() {
    console.log('V2 mounted!')
  }
};

В Vue3 Composition API функции-хуки жизненного цикла можно реализовать вsetup()функции setup() с использованиемonфункций с префиксом on:

Пример

import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })
    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};

В следующей таблице показано соответствие между Options API и Composition API, включая то, как вызывать хуки жизненного цикла внутри setup():

API Vue2 на основе OptionsVue Composition API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
errorCapturedonErrorCaptured

Поскольку setup выполняется вокруг хуков жизненного цикла beforeCreate и created, нет необходимости явно определять их. Иными словами, любой код, написанный в этих хуках, следует писать непосредственно в функции setup.

Эти функции принимают колбэк, который будет выполнен, когда компонент вызовет хук:

Пример

setup() {
...
    // Когда компонент смонтирован, мы используем хук onMounted, чтобы записать несколько сообщений
    onMounted(() => console.log('компонент смонтирован!'));
...
}

Ссылки на шаблон

При использовании Composition API реактивные ссылки и ссылки на шаблон — это единые концепции.

Чтобы получить ссылку на элемент или экземпляр компонента в шаблоне, мы можем объявить ref как обычно и вернуть его из setup():

Пример

<template>
  <div ref="root">Это корневой элемент</div>
</template>

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

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM-элемент будет присвоен ref после первоначального рендеринга
        console.log(root.value) // <div>Это корневой элемент</div>
      })

      return {
        root
      }
    }
  }
</script>

В приведённом выше примере мы предоставляем root в контексте рендеринга и привязываем его к div в качестве его ref с помощью ref="root".

Ссылки, используемые в качестве шаблонов, ведут себя как любой другой ref: они реактивны и могут передаваться в составные функции (или возвращаться из них).

Использование в v-for

При использовании внутри v-for ссылки на шаблон не обрабатываются специальным образом в Composition API. Вместо этого используйте function ref для пользовательской обработки:

Пример

<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

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

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // Не забудьте сбросить ref перед каждым обновлением
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>

Отслеживание ссылок на шаблон

Наблюдение за изменениями шаблонных ссылок может заменить хуки жизненного цикла, продемонстрированные в предыдущих примерах.

Однако ключевое отличие от хуков жизненного цикла состоит в том, что watch() и watchEffect() выполняются до монтирования или обновления DOM, что имеет побочные эффекты: когда наблюдатель запускается, шаблонные ссылки ещё не обновлены.

Пример

<template>
  <div ref="root">Это корневой элемент</div>
</template>

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

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        // Этот побочный эффект выполняется до обновления DOM, поэтому шаблонная ссылка ещё не содержит ссылку на элемент.
        console.log(root.value) // => null
      })

      return {
        root
      }
    }
  }
</script>

Поэтому наблюдатели, использующие шаблонные ссылки, должны определяться с опцией flush: 'post', которая запускает побочные эффекты после обновления DOM, гарантируя, что шаблонные ссылки остаются синхронизированными с DOM и ссылаются на правильный элемент.

Пример

<template>
  <div ref="root">Это корневой элемент</div>
</template>

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

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div>Это корневой элемент</div>
      },
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>

Справочник API

Порядковый номерAPI и описаниеПример
1setup()
Входная функция в Composition API компонента, вызываемая при создании экземпляра компонента и используемая для определения реактивных данных и методов.
export default { setup() { const count = ref(0); return { count }; } }
2ref()
Создайте реактивную ссылку для обёртывания примитивных типов или объектов.
const count = ref(0)
3computed()
Создайте вычисляемое значение на основе других реактивных данных.
const doubledCount = computed(() => count.value * 2)
4reactive()
Создать реактивный объект, подходящий для объектов или массивов.
const state = reactive({ count: 0 })
5readonly()
Создать реактивные данные только для чтения, запрещающие изменение.
const state = readonly({ count: 0 })
6watchEffect()
Создать реактивный побочный эффект, который автоматически отслеживает связанные с ним реактивные зависимости.
watchEffect(() => { console.log(count.value) })
7watchPostEffect()
Побочный эффект, срабатывающий после обновления DOM.
watchPostEffect(() => { console.log('Post effect') })
8watchSyncEffect()
Выполнять побочные эффекты синхронно при изменении реактивных зависимостей.
watchSyncEffect(() => { console.log('Synchronized effect') })
9watch()
Используется для наблюдения за изменениями реактивных данных; можно указать конкретные реактивные данные или вычисляемые свойства.
watch(count, (newCount) => { console.log(newCount) })
10onWatcherCleanup()
Очистить наблюдатель, вызываемый, когда наблюдаемые данные больше не нужны.
watch(count, () => {}, onWatcherCleanup(() => { console.log('cleaned up') }))
11isRef()
Проверить, относится ли значение кrefтипу ref.
isRef(count)
12unref()
Развернутьrefзначение ref.
unref(count)
13toRef()
Создать реактивный ref, извлечённый из объекта.
const countRef = toRef(state, 'count')
14toValue()
Развернутьrefилиreactiveзначение типа ref.
toValue(count)
15toRefs()
Преобразовать каждое свойство реактивного объекта в независимый ref.ref。
const { count } = toRefs(state)
16isProxy()
Проверить, является ли объект прокси-объектом Vue.
isProxy(state)
17isReactive()
Проверить, является ли объект реактивным объектом.
isReactive(state)
18isReadonly()
Проверить, является ли объект реактивным объектом, доступным только для чтения.
isReadonly(state)
19shallowRef()
Создать поверхностный реактивный объект,refделая реактивными только свойства первого уровня объекта.
const state = shallowRef({ count: 0 })
20triggerRef()
Принудительно вызватьrefобновление ref.
triggerRef(count)
21customRef()
Создать пользовательский реактивный ref, позволяющий разработчикам настраивать getter и setter.
const count = customRef((track, trigger) => { let value = 0; return { get: () => value, set: (val) => { value = val; trigger() } } })
22shallowReactive()
Создать поверхностный реактивный объект, в котором реактивны только свойства первого уровня объекта.
const state = shallowReactive({ count: 0 })
23shallowReadonly()
Создайте поверхностный реактивный объект только для чтения, где только свойства первого уровня объекта доступны только для чтения.
const state = shallowReadonly({ count: 0 })
24toRaw()
Получите исходный объект прокси-объекта.
const rawState = toRaw(state)
25markRaw()
Помечает объект как не подлежащий проксированию, и Vue пропустит реактивную обработку для этого объекта.
const obj = markRaw({ count: 0 })
26effectScope()
Создайте новую область видимости эффекта для управления побочными эффектами.
const scope = effectScope()
27getCurrentScope()
Получите текущую область видимости эффекта.
const scope = getCurrentScope()
28onScopeDispose()
Выполните функцию очистки при уничтожении области видимости эффекта.
onScopeDispose(() => { console.log('Scope disposed') })
29onMounted()
Хук жизненного цикла, вызываемый при монтировании компонента.
onMounted(() => { console.log('Component mounted') })
30onUpdated()
Хук жизненного цикла, вызываемый при обновлении компонента.
onUpdated(() => { console.log('Component updated') })
31onUnmounted()
Хук жизненного цикла, вызываемый при размонтировании компонента.
onUnmounted(() => { console.log('Component unmounted') })
32onBeforeMount()
Хук жизненного цикла, вызываемый перед монтированием компонента.
onBeforeMount(() => { console.log('Before mount') })
33onBeforeUpdate()
Хук жизненного цикла, вызываемый перед обновлением компонента.
onBeforeUpdate(() => { console.log('Before update') })
34onBeforeUnmount()
Хук жизненного цикла, вызываемый перед размонтированием компонента.
onBeforeUnmount(() => { console.log('Before unmount') })
35onErrorCaptured()
Перехват необработанных ошибок в дереве компонентов.
onErrorCaptured((err, instance, info) => { console.error(err) })
36onRenderTracked()
Хук, срабатывающий при отслеживании реактивных данных во время рендеринга компонента.
onRenderTracked((event) => { console.log(event) })
37onRenderTriggered()
Хук, срабатывающий при изменении реактивных данных во время рендеринга компонента.
onRenderTriggered((event) => { console.log(event) })
38onActivated()
Хук жизненного цикла, вызываемый при активации компонента (используется дляkeep-aliveкомпонента).
onActivated(() => { console.log('Component activated') })
39onDeactivated()
Хук жизненного цикла, вызываемый при деактивации компонента (используется дляkeep-aliveкомпонента).
onDeactivated(() => { console.log('Component deactivated') })
40onServerPrefetch()
Хук для предварительной загрузки данных, когда компонент рендерится на стороне сервера.
onServerPrefetch(async () => { await fetchData() })
41provide()
Предоставление зависимостей компонентам-потомкам.
provide('key', value)
42inject()
Внедрение зависимостей от родительских компонентов или компонентов-предков.
const value = inject('key')
43hasInjectionContext()
Проверка наличия контекста внедрения в данный момент.
hasInjectionContext()
44useAttrs()
Получение всех атрибутов компонента, включая HTML-атрибуты, переданные компоненту.
const attrs = useAttrs()
45useSlots()
Получение содержимого слота компонента.
const slots = useSlots()
46useModel()
Получить у текущего компонентаv-modelпривязанное значение.
const modelValue = useModel()
47useTemplateRef()
Получить ссылку на элемент в шаблоне.
const templateRef = useTemplateRef('elementId')
48useId()
Получить уникальный идентификатор.
const uniqueId = useId()
Другие расширения