Как получить доступ к корневому контексту из функции композиции в Vue Composition API / Vue 3.0 + TypeScript?
Я хочу создать многоразовую функцию-оболочку, написанную на TypeScript, для запуска всплывающего уведомления с помощью функции композиции, как определено в текущей спецификации Vue 3.0: Composition API RFC.
В этом примере используется компонент тоста BootstrapVue v2.0. В Vue 2 он будет вызываться черезthis.$bvToast
Внедрение экземпляра компонента Vue в корневой контекст:
this.$bvToast.toast('Error happened', {
title: 'Oh no',
variant: 'danger'
});
Эта служебная функция композиции будет выглядеть примерно так:
// File: @/util/notify.ts
export function useNotify() {
const notifyError = (title: string, msg: string) => {
// How to access context.root as in a function component, without passing it to this function?
context.root.$bvToast.toast(msg, {
title,
variant: 'danger'
});
};
return { notifyError};
}
export default useNotify;
И будет использоваться примерно так:
// Use in your functional component:
import { createComponent } from '@vue/composition-api';
import { useNotify} from '@/util/notify';
export default createComponent({
name: 'MyFailingComponent',
setup() {
const { notifyError } = useNotify();
notifyError('Request error', 'There was an error processing your request, please try again later.');
return {};
}
});
5 ответов
Что ж, вскоре я нашел подходящий пример на том же сайте RFC. Но решил поделиться здесь своими примерами.
Я полагаю, что в настоящий момент на сайте RFC нет примеров на TypeScript. Поскольку этот новый способ написания компонентов и функций композиции Vue 3.0 (как замена Mixins) требует некоторого привыкания.
Ответ: Вы можете передать объект контекста непосредственно в функцию композиции при деструктурировании необходимых частей в коде вашего компонента.
// File: @/util/notify.ts
// import { SetupContext } from '@vue/composition-api';
export function useNotify({ root }) {
const notifyError = (title: string, msg: string) => {
root.$bvToast.toast(msg, {
title,
variant: 'danger'
});
};
return { notifyError };
}
export default useNotify;
// Use in your functional component:
import { createComponent, SetupContext } from '@vue/composition-api';
import { useNotify} from '@/util/notify';
export default createComponent({
name: 'MyFailingComponent',
setup(props: any, context: SetupContext) {
const { notifyError } = useNotify(context);
notifyError('Request error', 'There was an error processing your request, please try again later.');
return {};
}
});
То же самое с типами TypeScript со сложной деструктуризацией объекта при передаче нескольких аргументов функции в виде объекта:
// File: @/util/notify.ts
import { SetupContext } from '@vue/composition-api';
export function useNotify({ context, defaultTitle = 'Hey!' }: { context: SetupContext, defaultTitle?: string }) {
const notifyError = (msg: string, title?: string) => {
context.root.$bvToast.toast(msg, {
title: title || defaultTitle,
variant: 'danger',
});
};
return {
notifyError,
};
}
export default useNotify;
// Usage like:
const { notifyError } = useNotify({ context });
// Or
const { notifyError } = useNotify({ context, defaultTitle: 'Hey there' });
Аккуратный синтаксис, отличное сообщество Vue!
Существует также:
import { getCurrentInstance } from 'vue' // or from '@vue/composition-api'
Это получит вызывающий компонент root
контекст из этого метода.
const root = getCurrentInstance(); // same as ctx.root in component
Вы можете в конечном итоге передать контекст каждому составному объекту, потому что их зависимости могут требовать сам контекст.
Существует альтернативное решение для предоставления корневого экземпляра без необходимости передавать его каждому компоненту, который у вас есть. Это немного упрощает их использование в компонентах:
Вы можете создать общий useRoot
composable и реализовать его с помощью функции Vue provide/inject:
// File: @/components/Root.js
// This is the app root
import useRoot from '@/composables/useRoot'
export default {
setup(props, context) {
const { provideRoot } = useRoot()
provideRoot(context.root)
}
}
// File: @/composables/useFancyStuff
// This is your composable (no arguments needed!)
import useRoot from '@/composables/useRoot'
export default function useNavigation() {
const { injectRoot } = useRoot()
const { $router } = injectRoot() // if you want to use the router
$router.push('/')
}
// File: @/composables/useRoot
// The implementation
import { provide, inject } from '@vue/composition-api'
const ProviderSymbol = Symbol()
export default function useRoot() {
const provideRoot = root => provide(ProviderSymbol, root)
const injectRoot = () => inject(ProviderSymbol)
return {
provideRoot,
injectRoot
}
}
В моем случае мне нужно было получить доступ кsetupContext
чтобы добраться до того же объекта, какsetup(props, { root }
.
export default function useContentManagerPostActions() {
const { emit, setupContext: { root } } = getCurrentInstance()
...
const addPost = async () => {
try {
...
} catch (error) {
...
root.$bvToast.toast('A new post could not be created.', {
title: 'Error',
variant: 'danger',
solid: false
})
}
}
РЕДАКТИРОВАТЬ: Это доступно только в NUXT !!!
Наclient-side
контекст доступен по адресуwindow.$vm.$root.context
Этот подход не будет работать на SSR!