Ref-доступ к компонентам

В библиотеке Naive UI взаимодействие с компонентами через ref является мощным инструментом для управления их состоянием и вызова методов напрямую из родительских компонентов. Использование ref особенно актуально в случаях, когда требуется программно управлять поведением компонентов, минуя реактивные свойства (props) и события (events).

Создание и привязка ref

Для того чтобы получить доступ к экземпляру компонента, необходимо использовать функцию ref из Vue 3 и передать ссылку на компонент через директиву ref в шаблоне:

<template>
  <n-button ref="buttonRef" @click="handleClick">Нажми меня</n-button>
</template>

<script setup>
import { ref } from 'vue';
import { NButton } from 'naive-ui';

const buttonRef = ref(null);

function handleClick() {
  console.log(buttonRef.value); // Доступ к экземпляру NButton
}
</script>

Ключевой момент: buttonRef.value содержит экземпляр компонента, через который можно вызывать его методы или работать с реактивными свойствами.

Доступ к методам компонента

Многие компоненты Naive UI предоставляют публичные методы, которые можно вызвать через ref. Например, компонент NInput имеет метод focus() для установки фокуса на поле ввода:

<template>
  <n-input ref="inputRef" placeholder="Введите текст"></n-input>
  <n-button @click="focusInput">Фокус</n-button>
</template>

<script setup>
import { ref } from 'vue';
import { NInput, NButton } from 'naive-ui';

const inputRef = ref(null);

function focusInput() {
  inputRef.value?.focus(); // Программное наведение фокуса на NInput
}
</script>

Использование ?. (optional chaining) важно для предотвращения ошибок в случае, если компонент ещё не смонтирован.

Типизация ref с TypeScript

Для корректной работы с методами компонентов в TypeScript необходимо указать тип экземпляра:

import { ref } from 'vue';
import type { NInputInst } from 'naive-ui';

const inputRef = ref<NInputInst | null>(null);

function focusInput() {
  inputRef.value?.focus();
}

Тип NInputInst содержит все публичные методы и свойства, доступные у компонента NInput. Аналогично существуют типы для других компонентов, например NButtonInst, NDropdownInst, NModalInst.

Использование ref с асинхронным рендерингом

Иногда доступ к компоненту через ref требуется после того, как он полностью смонтирован. В таких случаях удобно использовать хук onMounted:

import { ref, onMounted } from 'vue';
import { NInput } from 'naive-ui';

const inputRef = ref(null);

onMounted(() => {
  inputRef.value?.focus();
});

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

Доступ к внутренним элементам

Некоторые компоненты Naive UI предоставляют возможность получить доступ не только к методам компонента, но и к его внутренним элементам DOM. Например, NPopover можно программно открывать и закрывать через ref:

<template>
  <n-popover ref="popoverRef">
    <template #trigger>
      <n-button @click="togglePopover">Открыть</n-button>
    </template>
    Содержимое поповера
  </n-popover>
</template>

<script setup>
import { ref } from 'vue';
import { NPopover, NButton } from 'naive-ui';

const popoverRef = ref(null);

function togglePopover() {
  if (popoverRef.value) {
    popoverRef.value.show();
  }
}
</script>

Методы show() и hide() предоставляются через интерфейс компонента NPopoverInst.

Динамические ref для коллекций компонентов

При работе с коллекцией компонентов удобно использовать массив ref или ref с объектом, чтобы программно управлять несколькими экземплярами:

<template>
  <n-button v-for="(btn, index) in 3" :key="index" :ref="el => buttonRefs[index] = el">
    Кнопка {{ index + 1 }}
  </n-button>
</template>

<script setup>
import { ref } from 'vue';
import { NButton } from 'naive-ui';

const buttonRefs = [];

function clickAllButtons() {
  buttonRefs.forEach(btn => btn?.click());
}
</script>

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

Взаимодействие с ref и реактивными данными

Комбинируя ref и реактивные свойства (reactive или ref), можно создавать мощные динамические интерфейсы. Например, автоматическое обновление состояния компонента при изменении внешней переменной:

<template>
  <n-input ref="inputRef" v-model:value="text"></n-input>
  <n-button @click="clearInput">Очистить</n-button>
</template>

<script setup>
import { ref, watch } from 'vue';
import { NInput, NButton } from 'naive-ui';

const inputRef = ref(null);
const text = ref('Привет');

function clearInput() {
  text.value = '';
  inputRef.value?.focus();
}

watch(text, newVal => {
  console.log('Текущее значение поля:', newVal);
});
</script>

Такой подход обеспечивает прямой контроль над поведением компонентов без необходимости создавать сложные цепочки событий.

Резюме возможностей ref в Naive UI

  • Доступ к методам компонентов (focus(), show(), hide() и др.).
  • Программное управление состоянием компонентов.
  • Типизация в TypeScript через предоставленные интерфейсы (NInputInst, NButtonInst и т.д.).
  • Работа с динамическими коллекциями компонентов.
  • Совмещение с реактивными данными и наблюдателями (watch).
  • Безопасное использование с optional chaining и хуками жизненного цикла (onMounted).

Использование ref в Naive UI позволяет строить сложные интерактивные интерфейсы, сохраняя прямой и предсказуемый доступ к поведению каждого компонента.