如何从Vue Composition API / Vue 3.0 + TypeScript中的组合函数访问根上下文?

时间:2019-10-27 13:32:42

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

我想创建用 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 {};
  }
});

3 个答案:

答案 0 :(得分:5)

还有:

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

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

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

答案 1 :(得分: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
  }
}

答案 2 :(得分:1)

好吧,我很快在同一RFC站点上找到了一个正确的例子。但是决定在这里分享我的例子。

为了清晰起见,我假设RFC网站目前不在TypeScript中包含示例。随着这种新的Vue 3.0组件和组合功能(替代Mixins)的编写方式需要一些习惯。

答案:在将所需的部分分解为组件代码时,可以将上下文对象直接传递给composition函数。

// File: @/util/notify.ts
import { SetupContext } from '@vue/composition-api';

export function useNotify(context: SetupContext) {
  const notifyError = (title: string, msg: string) => {
    context.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社区做得很好!