useOverlay
一个用于程序化控制覆盖层的可组合项。
用法
使用自动导入的 useOverlay 可组合项来程序化控制 Modal 和 Slideover 组件。
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(LazyModalExample)
async function openModal() {
modal.open()
}
</script>
useOverlay可组合项是使用createSharedComposable创建的,确保在整个应用程序中共享相同的覆盖层状态。
为了从覆盖层返回一个值,可以
await overlay.open().instance.result。然而,为此,覆盖层组件必须发出一个 close 事件。详见下面的示例。API
create(component: T, options: OverlayOptions): OverlayInstance
创建一个覆盖层,并返回一个工厂实例。
- 参数:
component: 覆盖层组件。options:defaultOpen?: boolean创建后立即打开覆盖层。默认为false。props?: ComponentProps: 要传递给渲染组件的可选 props 对象。destroyOnClose?: boolean关闭时从内存中移除覆盖层。默认为false。
open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>
通过 id 打开一个覆盖层。
- 参数:
id: 覆盖层的标识符。props: 要传递给渲染组件的可选 props 对象。
close(id: symbol, value?: any): void
通过 id 关闭一个覆盖层。
- 参数:
id: 覆盖层的标识符。value: 用于解析覆盖层 Promise 的值。
patch(id: symbol, props: ComponentProps<T>): void
通过 id 更新一个覆盖层。
- 参数:
id: 覆盖层的标识符。props: 要在渲染组件上更新的 props 对象。
unmount(id: symbol): void
通过 id 从 DOM 中移除一个覆盖层。
- 参数:
id: 覆盖层的标识符。
isOpen(id: symbol): boolean
使用 id 检查覆盖层是否打开。
- 参数:
id: 覆盖层的标识符。
overlays: Overlay[]
所有已创建覆盖层的内存列表。
Instance API
open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>
打开覆盖层。
- 参数:
props: 要传递给渲染组件的可选 props 对象。
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(LazyModalExample)
function openModal() {
modal.open({
title: 'Welcome'
})
}
</script>
close(value?: any): void
关闭覆盖层。
- 参数:
value: 用于解析覆盖层 Promise 的值。
patch(props: ComponentProps<T>)
更新覆盖层的 props。
- 参数:
props: 要在渲染组件上更新的 props 对象。
<script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay()
const modal = overlay.create(LazyModalExample, {
title: 'Welcome'
})
function openModal() {
modal.open()
}
function updateModalTitle() {
modal.patch({ title: 'Updated Title' })
}
</script>
示例
这是一个如何使用 useOverlay 可组合项的完整示例:
<script setup lang="ts">
import { ModalA, ModalB, SlideoverA } from '#components'
const overlay = useOverlay()
// Create with default props
const modalA = overlay.create(ModalA, { title: 'Welcome' })
const modalB = overlay.create(ModalB)
const slideoverA = overlay.create(SlideoverA)
const openModalA = () => {
// Open modalA, but override the title prop
modalA.open({ title: 'Hello' })
}
const openModalB = async () => {
// Open modalB, and wait for its result
const modalBInstance = modalB.open()
const input = await modalBInstance.result
// Pass the result from modalB to the slideover, and open it
slideoverA.open({ input })
}
</script>
<template>
<button @click="openModalA">Open Modal</button>
</template>
在此示例中,我们使用 useOverlay 可组合项来控制多个模态框和抽屉。
注意事项
Provide / Inject
当程序化打开覆盖层(例如模态框、抽屉等)时,覆盖层组件只能访问包含 UApp 的组件(通常是 app.vue 或布局组件)注入的值。这是因为覆盖层是由 UApp 组件挂载在页面上下文之外的。
因此,在页面或父组件中使用 provide() 不受直接支持。要将提供的值传递给覆盖层,建议的方法是使用 props 代替:
<script setup lang="ts">
import { LazyModalExample } from '#components'
const providedValue = inject('valueProvidedInPage')
const modal = overlay.create(LazyModalExample, {
props: {
providedValue,
otherData: someValue
}
})
</script>
为什么覆盖层无法直接访问父组件的
provide()?<template>
<ConfigProvider :use-id="() => (useId() as string)" :dir="locale?.dir" :locale="locale?.code" v-bind="configProviderProps">
<TooltipProvider v-bind="tooltipProps">
<UToaster v-if="toaster !== null" v-bind="toasterProps">
<slot />
</UToaster>
<slot v-else />
<UOverlayProvider />
</TooltipProvider>
</ConfigProvider>
</template>
- 覆盖层的挂载位置独立于页面上下文
- 覆盖层(如模态框)是通过
UApp组件(通常在app.vue或布局组件中)挂载到 DOM 树顶层的,而非当前页面(Page)组件的子节点。 - 这意味着覆盖层脱离于当前页面组件的依赖注入上下文(不存在子孙层级关系),只能访问
UApp或其祖先组件提供的值,无法直接获取页面或父组件中通过provide()注入的值。
- 覆盖层(如模态框)是通过
- 依赖注入的作用域限制
- Vue 的
provide/inject机制是基于组件层级的。子组件仅能注入其 父组件链上 提供的值。覆盖层作为 UApp 的子组件,自然无法 “跨层级” 访问页面组件的注入值。
- Vue 的
slot 和 UOverlayProvider 组件不存在层级关系。