#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>
И все! Мы автоматически регистрируем компонент, и нашим пользователям не нужно вручную добавлять его в шаблон.