Возможно ли автоматически отображать компонент в vue 3?

#vue.js #vuejs3

#vue.js #vuejs3

Вопрос:

Я работаю над небольшой библиотекой для отображения уведомлений / тостов для vue 3. Моя идея состоит в том, чтобы добавить невидимый контейнер для моих уведомлений во время регистрации плагина. Поэтому конечный пользователь не должен заботиться о рендеринге этой области. Возможно ли это вообще?

Мой текущий плагин выглядит так:

 export const plugin = {
    install: (app: App, options?) => {
        options = reactive(options || defaultOptions);

        app.provide(symbol, instance);
        app.component('vue3-notification', Notification);
        app.component('vue3-notifications', Overlay);
        console.log('app', app); // app._component is null at this point
        var test = Overlay.render({ notifications: instance });
        console.log('test', test); // how to attach Overlay component to app?
    }
};
  

Похоже, что при установке плагина корневой контейнер vue еще недоступен. Мне удалось отобразить мой компонент, обеспечивающий необходимую зависимость (по крайней мере, я на это надеюсь, он зарегистрирован в консоли в последней строке), но я не знаю, как его смонтировать и интегрировать с основным приложением.

Мой компонент наложения, который я хочу автоматически отображать из плагина, выглядит следующим образом:

 <div class="notifications-overlay">
    <Teleport to="body">
        <vue3-notification
            v-for="(n, index) in notifications.stack.value"
            :key="n.id"
            v-bind="n"
            v-bind:hide="() => hide(n.id)"
        ></vue3-notification>
    </Teleport>
</div>
  

И он имеет фиксированную позицию:

 .notifications-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
}
  

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

Есть мысли?

Комментарии:

1. github.com/yariksav/vuetify-dialog создает определенный div для отображения уведомлений и тостов. Взгляните на их код, чтобы узнать, как они это сделали, мы просто используем их пакет….

Ответ №1:

В Vue 2 у нас есть Vue.extend для создания «подкласса» базового конструктора Vue, и это также позволяет вам монтировать экземпляр на элементе, что отлично подходит для этой цели.

Однако этот API был удален в Vue 3. Ознакомьтесь с RFC для глобальных изменений API.

Поскольку глобальный Vue больше не является новым конструктором, Vue.extend больше не имеет смысла с точки зрения расширения конструктора.

Хорошей новостью является то, что мы все еще можем достичь практически той же цели, используя createApp для визуализации нашего компонента плагина и mount его элемента DOM.

Если вам не нравится идея создания нескольких экземпляров Vue, вы можете проверить эту неофициальную библиотеку под названием mount-vue-component . Я сам не пробовал, но это позволяет монтировать компонент без использования createApp . Хотя, похоже, он использует некоторые внутренние свойства (например _context ), чтобы добиться цели. Я бы сказал, что все, что недокументировано, может измениться. Но привет.

Итак, вернемся к createApp подходу. И мы не будем использовать Teleport здесь. Следующие шаги — это только мои предпочтения, поэтому не стесняйтесь настраивать их в соответствии с вашим вариантом использования.

Добавление интерфейсов

 import { ComponentPublicInstance } from 'vue';

export interface INotify {
  (message: string): void;
}

export type CustomComponentPublicInstance = ComponentPublicInstance amp; {
  notify: INotify;
}
  

Мы используем тип пересечения для нашего экземпляра пользовательского компонента.

Реализация плагина

 import { App, createApp } from 'vue';
import Notifier from './path/to/component/Notifier.vue';

export const injectionKeyNotifier = Symbol('notifier');

export default {
  install(app: App) {
    const mountPoint = document.createElement('div');
    document.body.appendChild(mountPoint);

    const notifier = createApp(Notifier).mount(mountPoint) as CustomComponentPublicInstance;

    app.provide(injectionKeyNotifier, notifier.notify);
  }
}
  

На этом этапе нам просто нужно предоставить общедоступный метод (см. INotify Выше) из целевого компонента ( Notifier.vue ). Я вызываю этот метод notify . И он принимает строковый аргумент для сообщения.

Компонент: Notify.vue

 <template>
  <div class="my-notifier">
    <div class="msg" v-text="message"></div>
  </div>
</template>

<script lang="ts">
  import { defineComponent, ref } from 'vue';

  export default defineComponent(() => {
    const message = ref('');

    function notify(msg: string) {
      message.value = msg;
    }

    return {
      message,
      notify
    }
  })
</script>
  

Обратите внимание на вызываемую именованную функцию notify . Это общедоступный метод, о котором мы говорили ранее, и нам нужно его экспортировать.

Теперь use() это

В вашем файле ввода (например main.ts ):

 import { createApp } from 'vue'
import App from './App.vue'
import Notifier from 'my-custom-notifier'; // Assumes a library from the NPM registry (if you mean to publish it)

createApp(App)
  .use(Notifier)
  .mount('#app');
  

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

 <template>
  <div class="home">
    <button @click="showNotifs">Show notification</button>
  </div>
</template>

<script lang="ts">
  import { defineComponent, inject } from 'vue';
  import { INotify, injectionKeyNotifier } from 'my-custom-notifier';

  export default defineComponent({
    name: 'Home',

    setup() {
      const notify = inject(injectionKeyNotifier) as INotify;

      function showNotifs() {
        notify('You have x unread messages.');
      }

      return {
        showNotifs
      }
    }
  })
</script>
  

И все! Мы автоматически регистрируем компонент, и нашим пользователям не нужно вручную добавлять его в шаблон.