简体   繁体   English

如何在 Vue 组合 API / Vue 3.0 + TypeScript 中从组合 function 访问根上下文?

[英]How to access root context from a composition function in Vue Composition API / Vue 3.0 + TypeScript?

I want to create a reusable wrapper function written in TypeScript for triggering a toast notification by using a composition function , as defined in the current specification for Vue 3.0: Composition API RFC . I want to create a reusable wrapper function written in TypeScript for triggering a toast notification by using a composition function , as defined in the current specification for Vue 3.0: Composition API RFC .

This example is using BootstrapVue v2.0 toast component.本示例使用 BootstrapVue v2.0 toast 组件。 With Vue 2, it would be invoked via the this.$bvToast Vue component instance injection in the root context :使用 Vue 2,它将通过this.$bvToast Vue 组件实例注入在根上下文中调用:

this.$bvToast.toast('Error happened', {
  title: 'Oh no',
  variant: 'danger'
});

This service-like composition function would look much like this:这个类似服务的组合 function 看起来很像这样:

// 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;

And would be used much like this:并且会像这样使用:

// 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 {};
  }
});

Well, I soon found out a proper example on that same RFC site.好吧,我很快就在同一个 RFC 站点上找到了一个合适的示例。 But decided to share my examples here.但决定在这里分享我的例子。

The RFC site doesn't include examples in TypeScript at the moment, for clarity's sake I presume.为清楚起见,我认为 RFC 站点目前不包括 TypeScript 中的示例。 As this new way of writing Vue 3.0 components and composition functions (as a replacement to Mixins) takes a bit of getting used to.由于这种编写 Vue 3.0 组件和组合函数的新方式(作为 Mixins 的替代品)需要一些时间来适应。

Answer: You can pass the context object directly to the composition function when object-destructuring the needed parts into your component code.答:在将所需部分对象解构到组件代码中时,您可以将上下文 object 直接传递给组合 function。

// 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 {};
  }
});

Same using TypeScript types with complex object destructuring , when passing several function arguments as an object: Same using TypeScript types with complex object destructuring , when passing several function arguments as an object:

// 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' });

Neat syntax, well done Vue community!简洁的语法,干得好 Vue 社区!

There is also:还有:

import { getCurrentInstance } from 'vue'  // or from '@vue/composition-api'

This will get the calling component's root context from this method.这将从该方法获取调用组件的root上下文。

const root = getCurrentInstance();  // same as ctx.root in component

You might end up passing the context to every composable because their dependencies could require the context themselves.您最终可能会将上下文传递给每个可组合项,因为它们的依赖项可能需要上下文本身。

There is an alternative solution for providing the root instance without the need to pass it to every composable you have.有一种替代解决方案可以提供根实例,而无需将其传递给您拥有的每个可组合项。 This makes their usage in components a bit easier:这使得它们在组件中的使用更容易一些:

You can create a generic useRoot composable and implement it with Vue's provide/inject feature:您可以创建一个通用的useRoot组合并使用 Vue 的提供/注入功能来实现它:

// 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
  }
}

In my case I had to access the setupContext to get to the same object like setup(props, { root } .在我的情况下,我必须访问setupContext才能访问相同的 object ,例如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
      })
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM