【问题标题】:How to access root context from a composition function in Vue Composition API / Vue 3.0 + TypeScript?如何从 Vue Composition API / Vue 3.0 + TypeScript 中的组合函数访问根上下文?
【发布时间】:2020-02-23 01:36:56
【问题描述】:

我想创建一个用 TypeScript 编写的可重复使用的包装函数,用于通过使用 组合函数 来触发 toast 通知,如Vue 3.0 的当前规范:Composition API RFC

本示例使用 BootstrapVue v2.0 toast 组件。使用 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 {};
  }
});

【问题讨论】:

    标签: typescript vue.js bootstrap-vue vuejs3 vue-composition-api


    【解决方案1】:

    好吧,我很快就在同一个 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 社区干得好!

    【讨论】:

    • 我对此赞不绝口。谢谢你的好例子!
    【解决方案2】:

    您最终可能会将上下文传递给每个可组合对象,因为它们的依赖项可能需要上下文本身。

    有一种替代解决方案可以提供根实例,而无需将其传递给您拥有的每个可组合项。这使得它们在组件中的使用更容易一些:

    您可以创建一个通用的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
      }
    }
    

    【讨论】:

    • 我创建了 useRoot 可组合项,根据需要为另一个可组合项提供根 SetupContext(在应用程序组件中提供)。原来 Vue 给出了一个错误:[Vue warn]: inject() can only be used inside setup() or functional components.
    【解决方案3】:

    还有:

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

    这将从该方法中获取调用组件的root 上下文。

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

    【讨论】:

      猜你喜欢
      • 2023-01-10
      • 1970-01-01
      • 2022-12-04
      • 1970-01-01
      • 2020-09-22
      • 2021-05-31
      • 2020-11-11
      • 2022-11-28
      • 2022-11-29
      相关资源
      最近更新 更多