defineExpose() в Vue 3

6f7addd9b823f03077b833aea82b2e33.jpg

Привет, Хабр!

Сколько раз вы сталкивались с ситуацией, когда сделали аккуратный Vue-компонент на 

Теперь допустим, хочется вызвать logMessage из родителя:

childRef.value.logMessage(); // Ошибка: undefined

Это не сработает потому что script setup инкапсулирует всё внутри себя. Вся логика и данные компонента просто недоступны через ref.

В отличие от классического setup, где можно вернуть объект или использовать expose, script setup изначально ничего наружу не отдаёт. Vue рассматривает его как изолированную зону.

Что делает defineExpose () и как он работает

defineExpose() позволяет явно указать, какие переменные или методы компонента вы хотите сделать доступными родителю через ref.

Пример:

Теперь, если вы получите ref на этот компонент в родителе, вы сможете вызвать:

childRef.value.sayHi(); // всё работает!

Только то, что вы передадите в defineExpose, будет доступно. Всё остальное останется приватным, как и положено хорошему модульному коду.

Как это связано с ref на компонент

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

В setup родителя:

const childRef = ref(null);

onMounted(() => {
  childRef.value.sayHi(); // работает, если sayHi был передан через defineExpose
});

Если вы попытаетесь вызвать метод без defineExpose, childRef.value будет пустым объектом или вообще undefined.

Примеры применения

.focus () у поля ввода

Компонент:



Родитель:



Без defineExpose это бы не работало. Vue не стал бы передавать focus наружу.

.validate () в форме

Представим, что есть форма, в которой необходимо проверить корректность данных перед отправкой. Компонент формы:

В родителе:

if (formRef.value.validate()) {
  submitForm();
}

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

Фильтр с .reset ()

Часто в интерфейсах есть фильтры, которые пользователь должен сбрасывать по кнопке.

Родитель теперь может сбросить фильтры одним вызовом:

filterPanelRef.value.reset();

Как вообще работает defineExpose ()

Все таки с этим нужно разобраться немного подробнее.

Когда Vue создаёт экземпляр компонента, он формирует внутреннюю структуру — набор связей, реактивных переменных, методов, локального состояния. Всё это — приватное, изолированное. И тут важно: даже если ты определил ref, объявил функцию focus() или создал computed, родитель не получит доступ к этим штукам просто так через ref на компонент. Vue их не прокидывает наружу по умолчанию.

Именно для этого внутри у каждого компонента есть специальное хранилище — ctx.exposed. Это объект, который существует исключительно для того, чтобы родитель через ref.value мог достучаться до только тех методов и переменных, которые ты явно разрешил. В классическом setup() ты управляешь этим вручную — получаешь expose() из аргументов и сам передаёшь туда, что хочешь экспортировать:

setup(props, { expose }) {
  const focus = () => { input.value?.focus() }
  expose({ focus });
}

Когда ты используешь

Рейтинг@Mail.ru