跳到內容

createTemplatePromise

類別
匯出大小
387 B
最後變更
3 週前

將模板作為 Promise。適用於建構自訂對話框、模態視窗、Toast 等。

警告

此函數僅適用於 Vue 3

範例

用法

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>

特性

  • 程式化 - 將您的 UI 作為 promise 調用
  • 模板 - 使用 Vue 模板來渲染,而不是新的 DSL
  • TypeScript - 透過泛型類型實現完整的類型安全
  • 無渲染 - 您完全掌控 UI
  • 轉場效果 - 支援 Vue 轉場效果

此函數從 vue-template-promise 遷移而來

用法

createTemplatePromise 返回一個 Vue 元件,您可以直接在帶有 <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 函數。

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 方法。

ts
const result = await TemplatePromise.start()

一旦在模板中調用 resolvereject,promise 將被解析或拒絕,並返回您傳入的值。一旦解析,插槽將自動移除。

傳遞參數

您可以將參數傳遞給帶有參數的 start

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 屬性存取參數。

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>

轉場效果

您可以使用轉場效果來為插槽添加動畫。

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 轉場效果

動機

以程式化的方式調用對話框或模態視窗的常見方法如下

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

這會透過將這些資訊發送到頂層元件並讓它渲染對話框來運作。然而,這限制了您可以在 UI 中表達的靈活性。例如,您可能希望標題是紅色的,或者有額外的按鈕等等。您最終會得到很多像這樣的選項

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: () => {} },
  ],
  // ...
})

即使這樣也不夠靈活。如果您想要更多,您最終可能會使用手動渲染函數。

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

這就像在腳本中重新發明一種新的 DSL 來表達 UI 模板。

因此,此函數允許在模板中表達 UI 而不是在腳本中,這才是它應該在的地方,同時仍然能夠以程式化的方式進行操作。

類型宣告

顯示類型宣告
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.dev.org.tw/createTemplatePromise
 */
export declare function createTemplatePromise<Return, Args extends any[] = []>(
  options?: TemplatePromiseOptions,
): TemplatePromise<Return, Args>

原始碼

SourceDemo文件

貢獻者

Anthony Fu
Anthony Fu
Aaron-zon
Haoqun Jiang
Bruce

更新日誌

v12.0.0-beta.1 on 11/21/2024
0a9ed - feat!: 移除 Vue 2 支援,優化 bundle 並清理 (#4349)
v10.8.0 on 2/20/2024
a086e - fix: 更嚴格的類型
v10.1.1 on 5/1/2023
fc8cf - fix: 暫時繞過 vue 3.3 中的類型錯誤 (#3042)
v10.0.0-beta.5 on 4/13/2023
13169 - feat: 新函數 (#2957)

在 MIT 許可證下發布。