Vue 3 - получить доступ к экземпляру router-view для вызова дочерних методов

Я пытаюсь перенести приложение Vue 2.x на Vue 3.x. К сожалению, последние два дня я изо всех сил пытаюсь найти рабочее решение этой простой проблемы:

Мое приложение предназначено для мобильных устройств, и в верхней части экрана у меня есть верхняя панель с двумя контекстными кнопками слева и справа. Эти кнопки запускают методы, связанные с представлением, загруженным в мой <router-view/> и размещен в нем.

Мое приложение Vue 2 работало отлично, следуя советам этого поста :

[Vue 2] App.vue

      <template>
    <app-bar>
        <bar-btn @click="$refs.routerView[$route.meta.leftBtn.methodName]($route.meta.leftBtn.arguments)">Left btn</bar-btn>
        <div>View title</div>
        <bar-btn @click="$refs.routerView[$route.meta.rightBtn.methodName]($route.meta.rightBtn.arguments)">Right btn</bar-btn>
    </app-bar>
    <main>
        <router-view ref="routerView"/>
    </main>
</template>

Имена методов и необязательные аргументы, которые хранятся в моих метаданных моих маршрутов:

[Vue 2] Router.js

      {
    name: 'View 1',
    path: '/',
    component: MyView1,
    meta: {
        requiresAuth: false,
        leftBtn:  { methodName: 'showLeftDialog',  arguments: 'myArgument' }
        rightBtn: { methodName: 'showRightDialog', arguments: 'myArgument' }
    },
},

В Vue 2 у меня был доступ к экземпляру router-view с помощью: this.$refs.routerView

К сожалению, это больше не работает с Vue 3!

Потратив много времени, я не нашел надлежащего способа получить доступ к моему дочернему экземпляру, загруженному в мой <router-view/> для запуска моих методов, размещенных в нем.

[Vue 3] Это НЕ работает:

      Does not work:
this.$refs.routerView[this.$route.meta.leftBtn.methodName](this.$route.meta.leftBtn.arguments)

Does not work:
this.$router.currentRoute.value.matched[0].components.default.methods[this.$route.meta.leftBtn.methodName](this.$route.meta.leftBtn.arguments)

Does not work:
this.$refs.routerView.$refs    => this is an empty object

Проще говоря, как я могу получить доступ к экземпляру дочернего компонента, загруженному в режиме просмотра маршрутизатора с помощью Vue 3?

Любая помощь по этому поводу будет очень признательна.

2 ответа

Vue Router 4's <router-view> предоставляет визуализированный компонент представления в v-slotопора, которая может быть отображена с помощью <component>, к которому вы можете применить шаблон ref:

      <router-view v-slot="{ Component }">
  <component ref="view" :is="Component" />
</router-view>

Тогда доступ к методам компонента можно будет получить через $refs.view.$.ctx:

      <bar-btn @click="$refs.view.$.ctx[$route.meta.leftBtn.methodName]($route.meta.leftBtn.arguments)">Left btn</bar-btn>
<bar-btn @click="$refs.view.$.ctx[$route.meta.rightBtn.methodName]($route.meta.rightBtn.arguments)">Right btn</bar-btn>

демонстрация

У меня это не сработало, поэтому я сделал так:

// useEvent.js

      class Event {
    constructor(){
        this.events = {};
    }

    on(eventName, fn) {
        this.events[eventName] = this.events[eventName] || [];
        this.events[eventName].push(fn);
    }

    emit(eventName, data) {
        if (!this.events[eventName]) {
            throw new Error(`Event not registered: '${eventName}'`);
        }

        this.events[eventName].forEach(fn => fn(data));
    }
}

export default new Event();

Это решение похоже на глобальную шину событий Vue2.

Мы собираемся создать класс, который сможет прослушивать и генерировать данные из произвольных компонентов. Я бы посчитал это решение, вероятно, незаконным (территория анти-шаблонов во Vue 3) в большинстве случаев, кроме этого, когда мы пытаемся вызвать функцию в дочернем компоненте, в то время как дочерний элемент недоступен закомпонент.

// родительский

      <script setup>
import event from '@/composables/useEvent';

const someFunction = () => {
    event.emit('refetch-salmon', { yolo: true })
};
</script>

<template>
    <router-view :tab-height="tabHeight"></router-view>
</template>

// ребенок

      <script setup>
import { onMounted } from 'vue';
import event from '@/composables/useEvent';

onMounted(() => {
    event.on('refetch-salmon', data => fetchSalmon(data));
});
</script>

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

Другие вопросы по тегам