Skip to content

createTemplatePromise

模板作为 promise。对于构建自定义对话框、模态框、Toast 等很有用。

¥Template as Promise. Useful for constructing custom Dialogs, Modals, Toasts, etc.

警告

该函数仅适用于 Vue 3

¥This function only works for Vue 3

示例

用法

¥Usage

vue
<script setup lang="ts">
import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise<ReturnType>()

async function open() {
  const result = await TemplatePromise.start()
  // button is clicked, result is 'ok'
}
</script>

<template>
  <TemplatePromise v-slot="{ promise, resolve, reject, args }">
    <!-- your UI -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
</template>

特性

¥Features

  • 程序化 - 将你的 UI 称为 promise

    ¥Programmatic - call your UI as a promise

  • 模板 - 使用 Vue 模板来渲染,而不是新的 DSL

    ¥Template - use Vue template to render, not a new DSL

  • TypeScript - 通过泛型类型实现完整的类型安全

    ¥TypeScript - full type safety via generic type

  • 无渲染 - 你可以完全控制 UI

    ¥Renderless - you take full control of the UI

  • 转场 - 使用支持 Vue 转场

    ¥Transition - use support Vue transition

该函数是从 vue-template-promise 迁移而来

¥This function is migrated from vue-template-promise

用法

¥Usage

createTemplatePromise 返回一个 Vue 组件,你可以直接在模板中使用 <script setup> 来使用它

¥createTemplatePromise returns a Vue Component that you can directly use in your template with <script setup>

ts
import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise()
const MyPromise = createTemplatePromise<boolean>() // with generic type
js
import { createTemplatePromise } from '@vueuse/core'
const TemplatePromise = createTemplatePromise()
const MyPromise = createTemplatePromise() // with generic type

在模板中,使用 v-slot 来访问 promise 和 resolve 函数。

¥In template, use v-slot to access the promise and resolve functions.

vue
<template>
  <TemplatePromise v-slot="{ promise, resolve, reject, args }">
    <!-- you can have anything -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
  <MyPromise v-slot="{ promise, resolve, reject, args }">
    <!-- another one -->
  </MyPromise>
</template>

该槽最初不会被渲染(类似于 v-if="false"),直到你从组件调用 start 方法。

¥The slot will not be rendered initially (similar to v-if="false"), until you call the start method from the component.

ts
const result = await TemplatePromise.start()

一旦在模板中调用 resolvereject,promise 将被解析或拒绝,返回你传入的值。一旦解决,该插槽将自动删除。

¥Once resolve or reject is called in the template, the promise will be resolved or rejected, returning the value you passed in. Once resolved, the slot will be removed automatically.

传递参数

¥Passing Arguments

你可以使用参数将参数传递给 start

¥You can pass arguments to the start with arguments.

ts
import { createTemplatePromise } from '@vueuse/core'

const TemplatePromise = createTemplatePromise<boolean, [string, number]>()
js
import { createTemplatePromise } from '@vueuse/core'
const TemplatePromise = createTemplatePromise()
ts
const result = await TemplatePromise.start('hello', 123) // Pr

在模板槽中,你可以通过 args 属性访问参数。

¥And in the template slot, you can access the arguments via args property.

vue
<template>
  <TemplatePromise v-slot="{ args, resolve }">
    <div>{{ args[0] }}</div>
    <!-- hello -->
    <div>{{ args[1] }}</div>
    <!-- 123 -->
    <button @click="resolve(true)">
      OK
    </button>
  </TemplatePromise>
</template>

转场

¥Transition

你可以使用转场来为插槽设置动画。

¥You can use transition to animate the slot.

vue
<script setup lang="ts">
const TemplatePromise = createTemplatePromise<ReturnType>({
  transition: {
    name: 'fade',
    appear: true,
  },
})
</script>

<template>
  <TemplatePromise v-slot="{ resolve }">
    <!-- your UI -->
    <button @click="resolve('ok')">
      OK
    </button>
  </TemplatePromise>
</template>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

了解有关 Vue 转场 的更多信息。

¥Learn more about Vue Transition.

动机

¥Motivation

以编程方式调用对话框或模型的常见方法如下:

¥The common approach to call a dialog or a model programmatically would be like this:

ts
const dialog = useDialog()
const result = await dialog.open({
  title: 'Hello',
  content: 'World',
})

这可以通过将这些信息发送到顶层组件并让它渲染对话框来实现。但是,它限制了你在 UI 中表达的灵活性。例如,你可能希望标题为红色,或者有额外的按钮等。你最终会得到很多选项,例如:

¥This would work by sending these information to the top-level component and let it render the dialog. However, it limits the flexibility you could express in the UI. For example, you could want the title to be red, or have extra buttons, etc. You would end up with a lot of options like:

ts
const result = await dialog.open({
  title: 'Hello',
  titleClass: 'text-red',
  content: 'World',
  contentClass: 'text-blue text-sm',
  buttons: [
    { text: 'OK', class: 'bg-red', onClick: () => {} },
    { text: 'Cancel', class: 'bg-blue', onClick: () => {} },
  ],
  // ...
})

即使这样也不够灵活。如果你想要更多,你可能最终会使用手动渲染函数。

¥Even this is not flexible enough. If you want more, you might end up with manual render function.

ts
const result = await dialog.open({
  title: 'Hello',
  contentSlot: () => h(MyComponent, { content }),
})

这就像在脚本中重新发明一种新的 DSL 来表达 UI 模板。

¥This is like reinventing a new DSL in the script to express the UI template.

因此,此函数允许在模板而不是脚本中表达 UI,同时仍然能够以编程方式进行操作。

¥So this function allows expressing the UI in templates instead of scripts, where it is supposed to be, while still being able to be manipulated programmatically.

类型声明

显示类型声明
typescript
export interface TemplatePromiseProps<Return, Args extends any[] = []> {
  /**
   * The promise instance.
   */
  promise: Promise<Return> | undefined
  /**
   * Resolve the promise.
   */
  resolve: (v: Return | Promise<Return>) => void
  /**
   * Reject the promise.
   */
  reject: (v: any) => void
  /**
   * Arguments passed to TemplatePromise.start()
   */
  args: Args
  /**
   * Indicates if the promise is resolving.
   * When passing another promise to `resolve`, this will be set to `true` until the promise is resolved.
   */
  isResolving: boolean
  /**
   * Options passed to createTemplatePromise()
   */
  options: TemplatePromiseOptions
  /**
   * Unique key for list rendering.
   */
  key: number
}
export interface TemplatePromiseOptions {
  /**
   * Determines if the promise can be called only once at a time.
   *
   * @default false
   */
  singleton?: boolean
  /**
   * Transition props for the promise.
   */
  transition?: TransitionGroupProps
}
export type TemplatePromise<
  Return,
  Args extends any[] = [],
> = DefineComponent<object> & {
  new (): {
    $slots: {
      default: (_: TemplatePromiseProps<Return, Args>) => any
    }
  }
} & {
  start: (...args: Args) => Promise<Return>
}
/**
 * Creates a template promise component.
 *
 * @see https://vueuse.org/createTemplatePromise
 */
export declare function createTemplatePromise<Return, Args extends any[] = []>(
  options?: TemplatePromiseOptions,
): TemplatePromise<Return, Args>

源代码

源代码示例文档

变更日志

No recent changes

VueUse 中文网 - 粤ICP备13048890号