跳转到内容

Worker 看板

这个页面展示的是完整的 Worker 场景,而不是模拟预览。docs 页面负责 host DOM 和指针命中测试,remote Vue app 与看板状态则运行在专用 Web Worker 中。

这个示例包含三部分:

  • HostedTree 挂到预览区域中的 host runtime;
  • 渲染列与卡片、并处理 RemoteSortableEvent 的 remote Worker 应用;
  • 位于看板下方的实时 snapshot,用来显示每次 drop 之后的当前 remote state。
  • 只能通过 Drag handle 抓取卡片。
  • 在同一列的卡片之间移动可以重排,移到另一列可以完成跨列转移。
  • 需要回到初始状态时,使用 重置看板

运行在专用 Worker 中的 sortable 看板

通过 handle 拖拽卡片,在列内重排、跨列移动,并在需要时把示例重置回初始状态。

正在启动专用 Worker runtime...

这个看板接入的正是库对外公开 runtime API 所使用的同一套 host/worker transport 模型。

import type { Channel } from '@omnicajs/vue-remote/host'
import { createEndpoint, fromWebWorker } from '@remote-ui/rpc'
import {
createApp,
h,
} from 'vue'
import {
HostedTree,
createProvider,
createReceiver,
} from '@omnicajs/vue-remote/host'
interface SandboxWorkerApi {
release (): void;
run (channel: Channel, labels: SortableKanbanSandboxLabels): Promise<void>;
}
export interface SortableKanbanSandboxLabels {
booting: string;
failed: string;
ready: string;
resetLabel: string;
snapshotLabel: string;
snapshotUnavailable?: string;
unsupported: string;
}
export interface SortableKanbanSandboxElements {
rootElement: HTMLElement;
}
export interface SortableKanbanSandboxHandle {
destroy (): Promise<void>;
}
export const mountSortableKanbanSandbox = async (
elements: SortableKanbanSandboxElements,
labels: SortableKanbanSandboxLabels
): Promise<SortableKanbanSandboxHandle> => {
const { rootElement } = elements
if (typeof Worker === 'undefined') {
rootElement.textContent = labels.unsupported
return {
async destroy () {},
}
}
rootElement.textContent = labels.booting
const receiver = createReceiver()
const provider = createProvider()
const host = createApp({
render: () => h(HostedTree, { provider, receiver }),
})
const worker = new Worker(new URL('./remote.worker.ts', import.meta.url), {
type: 'module',
})
const endpoint = createEndpoint<SandboxWorkerApi>(fromWebWorker(worker))
let destroyed = false
const destroy = async () => {
if (destroyed) {
return
}
destroyed = true
try {
await endpoint.call.release()
} catch {
// Worker teardown should not block page cleanup.
} finally {
endpoint.terminate()
worker.terminate()
host.unmount()
rootElement.innerHTML = ''
}
}
try {
host.mount(rootElement)
await endpoint.call.run(receiver.receive, labels)
} catch (error) {
await destroy()
rootElement.textContent = `${labels.failed}: ${error instanceof Error ? error.message : String(error)}`
}
return {
destroy,
}
}